(2)和(3)是做这些事情的完全正常和惯用的方式。特别是关于 (2),您偶尔会听到的一种表达方式是“智能构造函数”。这只是意味着像您的mkFoo
/mkBar
那样的函数会产生 a FooBar a
(或 aMaybe (FooBar a)
等),并带有一些额外的逻辑,以确保只能构造合理的值。
以下是一些可能(或可能不会!)有意义的附加技巧,具体取决于您尝试使用FooBar
.
如果您在大多数情况下以相似的方式使用Foo
值和值(即拥有字段和字段Bar
之间的区别是一个小细节),那么将相似之处分解并使用单个构造函数是有意义的:Char
Int
data FooBar a = FooBar String FooBarTag [a]
data FooBarTag = Foo Char | Bar Int
除了在您不关心 时避免案例分析之外FooBarTag
,这还允许您安全地使用记录语法(具有多个构造函数的记录和类型不能很好地混合)。
data FooBar a = FooBar
{ fooBarName :: String
, fooBarTag :: FooBarTag
, fooBarList :: [a]
}
记录允许您使用字段,而不必对整个事物进行模式匹配。
如果 a 中的所有字段都有合理的默认值FooBar
,您可以超越mkFoo
-like 构造函数并定义默认值。
defaultFooBar :: FooBar a
defaultFooBar = FooBar
{ fooBarName = ""
, fooBarTag = Bar 0
, fooBarList = []
}
您不需要记录来使用默认值,但它们允许方便地覆盖默认字段。
myFooBar = defaultFooBar
{ fooBarTag = Foo 'x'
}
如果您厌倦了一遍又一遍地为默认值输入长名称,请考虑以下data-default
包:
instance Default (FooBar a) where
def = defaultFooBar
myFooBar = def { fooBarTag = Foo 'x' }
请注意,很多人不喜欢这Default
门课,而且不是没有理由的。尽管如此,对于非常特定于您的应用程序的类型(例如配置设置)Default
,IMO 非常好。
最后,更新记录字段可能会很麻烦。如果你最终对此感到恼火,你会发现它lens
非常有用。请注意,它是一个大图书馆,对于初学者来说可能有点不知所措,因此请先深呼吸。这是一个小样本:
{-# LANGUAGE TemplateHaskell #-} -- At the top of the file. Needed for makeLenses.
import Control.Lens
-- Note the underscores.
-- If you are going to use lenses, it is sensible not to export the field names.
data FooBar a = FooBar
{ _fooBarName :: String
, _fooBarTag :: FooBarTag
, _fooBarList :: [a]
}
makeLenses ''FooBar -- Defines lenses for the fields automatically.
defaultFooBar :: FooBar a
defaultFooBar = FooBar
{ _fooBarName = ""
, _fooBarTag = Bar 0
, _fooBarList = []
}
-- Using a lens (fooBarTag) to set a field without record syntax.
-- Note the lack of underscores in the name of the lens.
myFooBar = set fooBarTag (Foo 'x') defaultFooBar
-- Using a lens to access a field.
myTag = view fooBarTag myFooBar -- Results in Foo 'x'
-- Using a lens (fooBarList) to modify a field.
add :: a -> FooBar a -> FooBar a
add x fb = over fooBarList (x :) fb
-- set, view and over have operator equivalents, (.~). (^.) and (%~) respectively.
-- Note that (^.) is flipped with respect to view.
这是一个温和的介绍,lens
重点是我在这里没有展示的方面,特别是镜头可以如何组合。