【发布时间】:2016-11-07 07:33:38
【问题描述】:
我正在学习 Haskell,目前正在为数据成员发现“访问器”。 假设我有一些虚拟的 2D 顶点信息,一种类型有一些颜色,另一种类型有一些纹理坐标 (tc):
data SVertex = VertexC (Float, Float) Int
| VertexTC (Float, Float) (Float, Float)
deriving(Show)
创建记录访问器的一种乏味方法是编写带有模式的函数:
position (VertexC (x,y) c ) = (x,y)
position (VertexTC (x,y) c ) = (x,y)
tc (VertexTC _ tc) = tc
color :: SVertex -> Int
color (VertexC _ c) = c
现在,一个积极的功能是我可以为没有“color”或“tc”的访问器添加访问器(“color”和“tc”):
position (VertexC (x,y) c ) = (x,y)
position (VertexTC (x,y) c ) = (x,y) -- no header, here... still works
tc (VertexTC _ tc) = tc
tc (VertexC _ _) = (0,0) -- to returns something even if the field doesn't exist
color :: SVertex -> Int
color (VertexC _ c) = c
color (VertexTC _ _) = 0 -- return something even if field doesn't exist
它允许我为没有任何纹理坐标的顶点提供默认 0 值或为没有颜色的顶点提供颜色 0... 一切都好...
现在,我的问题是:我目前正在阅读有一种很好的方法可以将访问器名称直接添加到数据声明中。 就我而言,这是我会得到的(使用“prime”来避免名称冲突):
data SVertex' = VertexC' {
position' :: (Float, Float),
color' :: Int
}
| VertexTC' {
position' :: (Float, Float),
tc' :: (Float, Float)
} deriving(Show)
这让我达到了相同的目标:为我创建了“position'”、“tc'”和“color'”访问器!
然而:我没有找到为不存在的字段提供默认访问器的方法。例如,在 'VertexC' 上请求 tc 时;或在 VertexTC 上请求颜色... 在第一种方法中,我可以实现它。在这种方便的第二种方法中,我担心这是不可能的。 当我尝试添加其他功能模式时,例如
color' (VertexTC' _ _) = 0
编译器告诉我“‘color’等的多个声明”。似乎是因为第二个声明不是在编译器创建的前一个隐式声明之后完成的......
你知道解决方法吗?
【问题讨论】:
-
我不相信这是可能的。 Haskell 记录通常是一个痛点(尤其是它们是部分的,这就是你所得到的)。如果您对记录感兴趣,您可能最终会在某个时候查看
lens(使用Prism抽象处理这种偏颇性)。 -
这是不可能的,但它也是非惯用 Haskell 的一个指标。当您使用
SVertex实例来处理这些默认值时,请考虑更多地使用模式匹配,而不是制定全局规则(后一种方法感觉非常Java-esque)。 -
我没有意识到在 Haskell 中访问“记录”有这么大的缺陷。感谢您确认我应该如何意识到这一点。似乎有些人正在尝试在此范围内进行改进:nikita-volkov.github.io/record