2016-11-05 34 views
4

我学习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 

,一个积极的特点是,我能为那些没有“色”或“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的顶点,没有颜色... 所有好...

现在,我的问题:我现在正在阅读,有一个很好的方法可以将访问者名称直接放入数据声明中。 在我这里的情况是什么,我会(使用“黄金”,以避免名称冲突)获得:

data SVertex' = VertexC' { 
    position' :: (Float, Float), 
    color'  :: Int 
    } 
    | VertexTC' { 
    position' :: (Float, Float), 
    tc'   :: (Float, Float) 
    } deriving(Show) 

这样我就可以达到同样的目标:“位置“”,“TC””和“色''访问器是为我创建的!

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

color' (VertexTC' _ _) = 0 

编译器告诉我“‘色’等多次声明”。而现在看来似乎是因为本次声明没有做以下由编译器生成以前隐含一个...

你知道解决方法吗?

+2

我不相信这是可能的。一般来说,Haskell记录是一个痛苦点(特别是它们是部分的,这就是你所得到的)。如果你对记录感兴趣,你可能最终会在某个时候检查出['lens'](https://hackage.haskell.org/package/lens)(这个偏好是用“Prism”抽象来处理的)。 – Alec

+1

这是不可能的,但它也是非惯用Haskell的一个指标。当你使用'SVertex'实例来处理这些默认值时,考虑更多地使用模式匹配,而不是制定一个全局规则(后一种方法感觉Java非常流行)。 –

+0

我没有意识到访问'记录'在Haskell中存在如此的缺陷。感谢您确认我应该如何意识到这一点。 似乎有些人正试图改善这个范围内的事情:http://nikita-volkov.github.io/record/ –

回答

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 

亚历克所暗示的,lens库提供了大量的工具来应对这种情况更方便。在任何情况下,在这个答案中定义的类型可以很好地与镜头配合使用。