Haskell数据类型中的默认值

Dra*_*gno 10 haskell

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

提前致谢

chi*_*chi 10

常用的习惯用法是定义默认值.

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

defaultA :: A
defaultA = A{foo = 0, bar = ""}
Run Code Online (Sandbox Code Playgroud)

然后可以(纯粹地)使用实际值"更新".

doSomething :: Bool -> A
doSomething True  = defaultA{foo = 32}
doSomething False = defaultA{bar = "hello!"}
Run Code Online (Sandbox Code Playgroud)

伪代码示例:

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 }
Run Code Online (Sandbox Code Playgroud)

如果没有合理的默认值,则可以将字段值换行Maybe.

如果您喜欢冒险,您甚至可以使用更高级的方法静态地分离"中间"选项值,这些值可能缺少一些字段,而"最终"选项值必须包含所有字段.(不过,我不会向Haskell初学者推荐这个.)


Wil*_*sem 8

Haskell中是否有任何机制可以在记录类型中执行相同的操作?

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

比如我们有一个我们想要更新的列表,以及一个修订号,然后我们可以将它定义为:

data RevisionList a = RevisionList { theList :: [a],
                                     revision :: Int }
                          deriving Show
Run Code Online (Sandbox Code Playgroud)

现在我们可以定义一个BuildList用初始列表初始化的函数:

revisionList :: [a] -> RevisionList a
revisionList xs = RevisionList { theList = xs, revision=0 }
Run Code Online (Sandbox Code Playgroud)

并且通过将构造函数隐藏在module导出中,我们因此隐藏了使用另一个修订而不是修订来初始化它的可能性0.所以模块看起来像:

module Foo(RevisionList(), revisionList)

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

revisionList :: [a] -> RevisionList a
revisionList xs = RevisionList { theList = xs, revision=0 }
Run Code Online (Sandbox Code Playgroud)

类似OOP的构建模式?

例如,我们可以使用Statemonad.例如:

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
Run Code Online (Sandbox Code Playgroud)

因此,我们getRevisionList迄今进行更新,put新的结果返回.

所以现在另一个模块可以导入我们的Foo,并使用如下的构建器:

some_building :: RevisionListBuilder Int ()
some_building = do
    prefixList 4
    prefixList 1
Run Code Online (Sandbox Code Playgroud)

现在我们可以"修改" 作为最终列表进行RevisionList修订: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])
Run Code Online (Sandbox Code Playgroud)

所以它看起来大致如下:

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
Run Code Online (Sandbox Code Playgroud)

Bar.hs:

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])
Run Code Online (Sandbox Code Playgroud)

所以现在我们构建了一个some_rev_list"建筑" some_building:

Foo Bar> some_rev_list 
RevisionList {theList = [1,4,2,5], revision = 2}
Run Code Online (Sandbox Code Playgroud)