Haskell 不存在记录的访问器
Haskell accessors for non-existing records
我正在学习 Haskell,目前正在为数据成员寻找 'accessors'。
假设我有一些虚拟 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
编译器告诉我"Multiple declarations of ‘color’ etc."。似乎是因为第二个声明没有在编译器创建的前一个隐式声明之后完成...
您知道解决方法吗?
正如您刚刚发现的那样,记录不能很好地与求和类型(即具有多个构造函数的类型)混合,因为它们会导致您无法摆脱的不愉快的部分访问器。一种替代方法是仅对实际需要它的字段使用求和类型,而不是将 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 库提供了大量工具来以更方便的方式处理此类情况。在任何情况下,此答案中定义的类型都适用于 lens.
我正在学习 Haskell,目前正在为数据成员寻找 'accessors'。 假设我有一些虚拟 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
编译器告诉我"Multiple declarations of ‘color’ etc."。似乎是因为第二个声明没有在编译器创建的前一个隐式声明之后完成...
您知道解决方法吗?
正如您刚刚发现的那样,记录不能很好地与求和类型(即具有多个构造函数的类型)混合,因为它们会导致您无法摆脱的不愉快的部分访问器。一种替代方法是仅对实际需要它的字段使用求和类型,而不是将 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 库提供了大量工具来以更方便的方式处理此类情况。在任何情况下,此答案中定义的类型都适用于 lens.