使用typeclass访问Haskell中类似数据类型的字段

使用typeclass访问Haskell中类似数据类型的字段,haskell,graph,polymorphism,typeclass,Haskell,Graph,Polymorphism,Typeclass,我正在研究Haskell中的一些图问题。在我的工作中,我决定我希望能够代表图形数据类型中的边色。所以我从边缘开始:边缘可以是彩色的,也可以是非彩色的。这是一个我所想的快速模型。请记住,我知道这段代码中存在严重的缺陷 data BasicEdge v w = BasicEdge { b_endpoints :: (v,v), b_weight :: w} data ColoredEdge v w c = ColoredEdge { c_endpoints :: (v,v), c_weight ::

我正在研究Haskell中的一些图问题。在我的工作中,我决定我希望能够代表图形数据类型中的边色。所以我从边缘开始:边缘可以是彩色的,也可以是非彩色的。这是一个我所想的快速模型。请记住,我知道这段代码中存在严重的缺陷

data BasicEdge v w = BasicEdge { b_endpoints :: (v,v), b_weight :: w}
data ColoredEdge v w c = ColoredEdge { c_endpoints :: (v,v), c_weight :: w, color :: c}

class Edge e where
    endpoints :: e -> (v,v)
    weight :: e -> w

instance Edge (BasicEdge v w) where
    endpoints = b_endpoints
    weight = b_weight

instance Edge (ColoredEdge v w c) where
    endpoints = c_endpoints
    weight = c_weight
问题1:BasicEdge中的
v
w
与ColoreEdge中的
v
w
是不同的类型变量。因此,试图以多态方式访问它们是荒谬的

问题2:边缘类定义中的返回值是自由类型变量,因此它们无法与
b_端点
c_端点
等的返回值匹配

我确实需要类型变量-顶点可以是字符、字符串、整数等。。边权重可以是任何类型的数字(浮动有助于解决某些问题)。颜色甚至可以是构造的数据类型

在语言中有没有一种“惯用”的方法来做到这一点?这似乎是多态性的一种基本类型,但我很难理解如何实现它


提前感谢您的帮助,请理解我在过去的一天里一直试图在互联网上搜索指导。很难为此问题构造搜索查询。

可以通过在类定义中包含类型参数
v
w
来修复类型类

class Edge e where
    endpoints :: e v w -> (v,v)
    weight :: e v w -> w
现在
e
具有
*->*->*->*
类型,这意味着它需要是一个具有两个附加类型参数的类型,然后这些参数在
端点中使用
权重
,以实际将结果类型链接到边的类型

但是,您需要稍微调整
ColoredEdge
类型,以便
v
w
是最后两个参数,因此

data BasicEdge v w = BasicEdge { b_endpoints :: (v,v), b_weight :: w}
data ColoredEdge c v w = ColoredEdge { c_endpoints :: (v,v), c_weight :: w, color :: c}
现在可以将实例定义为

instance Edge BasicEdge where
    endpoints = b_endpoints
    weight = b_weight

instance Edge (ColoredEdge c) where
    endpoints = c_endpoints
    weight = c_weight
另一个选项是使用
TypeFamilies
语言扩展,并在
Edge
类中使顶点和权重类型关联类型同义词。这样,实例类型中的类型参数顺序就变得无关紧要了

{-# LANGUAGE TypeFamilies #-}

class Edge e where
    type Vertex e
    type Weight e

    endpoints :: e -> (Vertex e, Vertex e)
    weight :: e -> Weight e

instance Edge (BasicEdge v w) where
    type Vertex (BasicEdge v w) = v
    type Weight (BasicEdge v w) = w

    endpoints = b_endpoints
    weight = b_weight

instance Edge (ColoredEdge v w c) where
    type Vertex (ColoredEdge v w c) = v
    type Weight (ColoredEdge v w c) = w

    endpoints = c_endpoints
    weight = c_weight
然而,类型类通常不是这种多态性的最佳解决方案。对于任何附加数据,我只需在
Edge
类型中包含一个额外的参数,如

data Edge v w d = Edge { endpoints :: (v,v), weight :: w, edgeData :: d }
现在,您可以在
d
中添加颜色,或者在包含边的多个数据字段的记录中添加颜色,并且仍然可以以通用方式查询图形的形状

您的原始边类型现在可以用类型同义词表示

type BasicEdge   v w   = Edge v w ()
type ColoredEdge v w c = Edge v w c

扪心自问:编译器应该如何知道
v
是什么类型?我知道这是一个问题-请参阅我问题中的“问题1”和“问题2”。挑战在于找到一个很好的解决方案。那么,在这种情况下,类型族将是理想的。如果您想了解我的意思,请查看
GHC.Exts
软件包的
IsList
类。我开始写答案,当时我忙于描述FundeP和ATs,以至于完全错过了显而易见的Haskell 2010解决方案。打得好!哇!想到将一个未使用的类型变量放在一个简单的边缘,我非常不安,出于某种原因,我没有想到我可以用一个类型同义词来隐藏它。虽然您的第一个答案是有道理的,但我更愿意走极简路线——正如您所说,在这种情况下使用类型类感觉不太正确。非常感谢你的帮助。我非常喜欢类型家庭。很好的答案,很好的例子。