Haskell 使用记录语法中的大量构造函数替代代数数据类型
我有一个自定义数据类型来包含景观元素(云、太阳、山等)。我需要它们的列表,所以我不能使用不同的类型和通用的类型类 它们共享构造函数中的大部分字段,但有些字段具有其他字段不具有的属性(例如,如果云层正在下雨或没有下雨) 到目前为止,我有一种具有不同构造函数的数据类型:Haskell 使用记录语法中的大量构造函数替代代数数据类型,haskell,record,algebraic-data-types,Haskell,Record,Algebraic Data Types,我有一个自定义数据类型来包含景观元素(云、太阳、山等)。我需要它们的列表,所以我不能使用不同的类型和通用的类型类 它们共享构造函数中的大部分字段,但有些字段具有其他字段不具有的属性(例如,如果云层正在下雨或没有下雨) 到目前为止,我有一种具有不同构造函数的数据类型: data Element = Sun { elemColorStart :: Color, elemColorEnd :: Color, elemCoords :: Coords, elemPeriod :: Flo
data Element = Sun {
elemColorStart :: Color,
elemColorEnd :: Color,
elemCoords :: Coords,
elemPeriod :: Float,
elemSize :: Float,
elemSteps :: Step,
elemTime :: Float
}
| Cloud {
elemKind :: CloudKind,
elemColorMain :: Color,
elemCoords :: Coords,
elemRans :: [Float],
elemSize' :: Size',
elemSteps :: Step,
elemTime :: Float
}
... etc
或者,我可以有一个具有所有可能属性的公共构造函数,如果不需要它们,就不初始化它们,尽管这看起来更糟
这看起来不是很“Haskell”,事实上,这种方法通常是面向对象的我做错了什么?欢迎任何其他可能的方法;这不是一个赋值,所以我没有任何约束。将事物隔离到它们自己的数据类型中,并让
元素成为它们的总和:
data Sun = Sun {
colorStart :: Color,
colorEnd :: Color,
coords :: Coords,
period :: Float,
size :: Float,
steps :: Step,
time :: Float
}
data Cloud = Cloud {
kind :: CloudKind,
colorMain :: Color,
coords :: Coords,
rans :: [Float],
size :: Size',
steps :: Step,
time :: Float
}
data Element = SunElement Sun | CloudElement Cloud
现在,您可以为独立的事物提供专用的API,就像关注点分离带来的所有好处一样
哦,顺便说一句,我删除了字段名的前缀,因为现在我们有了DuplicateRecordFields
扩展名
顺便说一句,你会发现它很有用。Nikita的答案的一个转折点是使用一个名为Shared
的ADT来保存在Sun
和Cloud
中找到的字段。如果您愿意,可以将其与OO的继承进行比较
data Shared = Shared {
coords :: Coords,
steps :: Step,
time :: Float
}
data Sun = Sun {
colorStart :: Color,
colorEnd :: Color,
period :: Float,
size :: Float,
shared :: Shared
}
data Cloud = Cloud {
kind :: CloudKind,
colorMain :: Color,
rans :: [Float],
size :: Size',
shared :: Shared
}
data Element = SunElement Sun | CloudElement Cloud
回答得好,我从没想过。但是前缀是存在的,因为我不想用size
或time
之类的东西污染全局名称空间,我可能需要这些东西来做其他事情。有什么简单的解决方案吗?@Lorenzo如果你不想污染名称空间,你要么需要前缀(就像你做的那样),要么在一个单独的模块中定义它们,然后将合格的MyModule导入为M
,然后使用M.field
。后者不是非常方便,我认为如果一个人有很多类型要添加到模块中就可以了。我会严格限制sum类型的构造函数。如果类型确实是性能关键型的,那么您可能希望使用{-#UNPACK#-}
。通过数据共享=共享{steps::Step,time::Float,coords::coords}
?@Lorenzo引入支持,使其更严格:数据元素=SunElement!太阳|云元素!云
。这意味着要生成一个元素
,您需要有一个太阳
或云
,而不仅仅是一个计算一个元素的thunk。语义与原始类型相同,而不是更懒惰,并且不太可能泄漏内存。解包:data Element=SunElement{-#解包#-}!Sun | CloudElement{-#UNPACK}!云
。这将把记录解压到sum类型构造函数中,而不是有一个指向它的指针。这在操作上与您的原始版本完全相同。(续)