不存在记录的Haskell访问器

时间:2016-11-05 23:46:01

标签: haskell accessor

我正在学习Haskell,而我目前正在发现“访问者”#39;对于数据成员。 让我们假设我有一些虚拟的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

现在,一个积极的功能是,我可以添加访问者('颜色'' tc')为那些没有颜色' ;或者' 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)

这使我能够实现同一目标:"位置' "," tc' "和"颜色' "为我创建了访问者!

然而:我没有找到为不存在的字段提供默认访问器的方法。例如,在VertexC'上请求tc时;或者在VertexTC上请求颜色...... 在第一种方法中,我可以实现它。在这方便的第二种方法中,我担心这是不可能的。 当我尝试添加其他功能模式,如

color' (VertexTC' _ _) = 0

编译器告诉我"'color'等多个声明" 。它似乎是因为第二个声明不是在编译器创建的前一个隐式声明之后完成的......

你知道解决方法吗?

1 个答案:

答案 0 :(得分:4)

正如您刚刚发现的那样,记录不能与总和类型(即具有多个构造函数的类型)很好地混合,因为它们会导致您无法摆脱的令人不快的部分访问者。一种替代方法是仅对实际需要它的字段使用和类型,而不是将SVertex作为整体使用。这样,您可以获得尽可能多的不错访问者,同时避免使用部分访问者。

data VertexPaint = VertexC Int | VertexTC (Float, Float)
    deriving (Show)

data SVertex = SVertex
    { position :: (Float, Float)
    , paintjob :: VertexPaint
    } deriving (Show)

如果你想要一个color功能,你仍然需要单独定义它,就像你第一次尝试一样。 (这里我将使用Maybe Int结果,因为这通常比返回任意默认值更安全。)

color :: SVertex -> Maybe Int
color v = case paintjob v of
    VertexC c -> Just c
    VertexTC _ -> Nothing

正如Alec建议的那样,lens库提供了大量工具来以更方便的方式处理这种情况。在任何情况下,此答案中定义的类型都适用于镜头