在面向对象语言中定义类时,它通常会设置成员变量的默认值.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初学者推荐这个.)
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的构建模式?
例如,我们可以使用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
Run Code Online (Sandbox Code Playgroud)
因此,我们get
在RevisionList
迄今进行更新,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)
归档时间: |
|
查看次数: |
656 次 |
最近记录: |