Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell数据类型中的默认值_Haskell_Types - Fatal编程技术网

Haskell数据类型中的默认值

Haskell数据类型中的默认值,haskell,types,Haskell,Types,用面向对象语言定义类时,它通常为成员变量设置默认值。Haskell中是否有机制在记录类型中执行相同的操作?还有一个后续问题:如果我们从一开始就不知道数据构造函数的所有值,但我们从IO交互中获得它们,那么我们可以使用OOP中的生成器模式之类的东西来构建类型吗 提前感谢一个常见的习惯用法是定义默认值 data A = A { foo :: Int , bar :: String } defaultA :: A defaultA = A{foo = 0, bar = ""} 然后可以(纯粹地)“更

用面向对象语言定义类时,它通常为成员变量设置默认值。Haskell中是否有机制在记录类型中执行相同的操作?还有一个后续问题:如果我们从一开始就不知道数据构造函数的所有值,但我们从IO交互中获得它们,那么我们可以使用OOP中的生成器模式之类的东西来构建类型吗


提前感谢

一个常见的习惯用法是定义默认值

data A = A { foo :: Int , bar :: String }

defaultA :: A
defaultA = A{foo = 0, bar = ""}
然后可以(纯粹地)“更新”实际值

doSomething :: Bool -> A
doSomething True  = defaultA{foo = 32}
doSomething False = defaultA{bar = "hello!"}
伪代码示例:

data Options = O{ textColor :: Bool, textSize :: Int, ... }

defaultOptions :: Options
defaultOptions = O{...}

doStuff :: Options -> IO ()
doStuff opt = ...

main :: IO ()
main = do
     ...
     -- B&W, but use default text size
     doStuff defaultOptions{ color = False }
如果没有合理的默认值,可以将字段值包装在
中,也可以包装在

如果你喜欢冒险,你甚至可以使用一个静态的“中间”选项值,它可能缺少几个字段,而“最终”选项值必须包含所有字段。(不过,我不推荐Haskell初学者使用此功能。)

Haskell中是否有机制在记录类型中执行相同的操作

您可以做的是隐藏构造函数,并提供一个函数作为构造函数

例如,假设我们有一个要更新的列表,以及一个修订号,那么我们可以将其定义为:

data RevisionList a = RevisionList { theList :: [a],
                                     revision :: Int }
                          deriving Show
现在,我们可以定义一个函数,用一个初始列表初始化
BuildList

revisionList :: [a] -> RevisionList a
revisionList xs = RevisionList { theList = xs, revision=0 }
通过将构造函数隐藏在
模块
导出中,我们可以隐藏使用另一个修订版而不是修订版
0
初始化构造函数的可能性。因此,模块可能看起来像:

module Foo(RevisionList(), revisionList)

data RevisionList a = RevisionList { theList :: [a],
                                     revision :: Int }

revisionList :: [a] -> RevisionList a
revisionList xs = RevisionList { theList = xs, revision=0 }
类似于OOP中的构建器模式

例如,我们可以使用
State
monad来实现这一点。例如:

module Foo(RevisionList(), revisionList,
           increvision, RevisionListBuilder, prefixList)

import Control.Monad.State.Lazy

type RevisionListBuilder a = State (RevisionList a)

increvision :: RevisionListBuilder a ()
increvision = do
    rl <- get
    put (rl { revision = 1 + revision rl})

prefixList :: a -> RevisionListBuilder a ()
prefixList x = do
    rl <- get
    put (rl { theList = x : theList rl })
    increvision
现在,我们可以在修订版
2
中“制作”一份
修订列表,作为最终列表
[1,4,2,5]

import Control.Monad.State.Lazy(execState)

some_rev_list :: RevisionList Int
some_rev_list = execState some_building (revisionList [2,5])
所以它看起来大概像:

Foo.hs

module Foo(RevisionList(), revisionList,
           increvision, RevisionListBuilder, prefixList)

data RevisionList a = RevisionList { theList :: [a],
                                     revision :: Int }
                          deriving Show
type RevisionListBuilder a = State (RevisionList a)

revisionList :: [a] -> RevisionList a
revisionList xs = RevisionList { theList = xs, revision=0 }

increvision :: RevisionListBuilder a ()
increvision = do
    rl <- get
    put (rl { revision = 1 + revision rl})

prefixList :: a -> RevisionListBuilder a ()
prefixList x = do
    rl <- get
    put (rl { theList = x : theList rl })
    increvision
import Foo
import Control.Monad.State.Lazy(execState)

some_building :: RevisionListBuilder Int ()
some_building = do
    prefixList 4
    prefixList 1

some_rev_list :: RevisionList Int
some_rev_list = execState some_building (revisionList [2,5])
因此,现在我们构建了一个
some\u rev\u列表
,其中“building”为
some\u building

Foo Bar> some_rev_list 
RevisionList {theList = [1,4,2,5], revision = 2}

这里已经有了很好的答案,所以这个答案只是对chi和Willem Van Onsem的好答案的补充

在Java和C#等主流面向对象语言中,并不是默认对象未初始化;相反,默认对象通常使用其类型的默认值进行初始化,而对于引用类型,默认值是空引用

Haskell没有空引用,因此不能用空值初始化记录。对象最直接的转换是记录,其中每个组成元素都是
可能
。然而,这并不是特别有用,但它强调了在OOP中保护不变量是多么困难

构建器模式根本不能解决这个问题。任何生成器都必须以初始生成器对象开始,并且该对象也必须具有默认值


为了获得更多的细节和大量的例子,我写了一篇文章。本系列文章特别关注测试数据生成器模式,但您应该能够看到它是如何概括为Fluent Builder模式的。

还有
Default
typeclass,您可以使用
DeriveAnyClass
DeriveGeneric
派生它。顺便说一句,它将导出与
defaultA
相同的默认值;具有中性行为的值。此说明适用于中的
mempty
。如果这些类型是幺半群,那么
A
Options
的默认值可以是标识值,而它们可以是幺半群。你不能构建一个对象,并在获得更多输入时逐渐修改它,因为你不能修改任何东西。构建器模式没有多大用处:您只需执行所有IO,然后一旦获得所需的值,就可以通过构造函数构建值。
Foo Bar> some_rev_list 
RevisionList {theList = [1,4,2,5], revision = 2}