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

Mor*_*ire 4 polymorphism haskell graph 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 :: 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
Run Code Online (Sandbox Code Playgroud)

问题1:vw在BasicEdge比不同类型的变量vw在ColoredEdge.因此,试图以多态方式访问它们是荒谬的.

问题2:在边缘类定义的返回值是游离型的变量,所以他们不能用的返回值进行匹配b_endpointsc_endpoints等.

我确实需要类型变量 - 顶点可以是字符,字符串,整数等.边缘权重可以是任何类型的数字(浮点数对某些问题有帮助).颜色甚至可以是构造的数据类型.

是否有一种"惯用"的方式来用语言做到这一点?看来这是一种基本类型的多态,但我很难理解如何实现它.

在此先感谢您的帮助,请理解我在过去的一天中尝试在互联网上搜索指导.很难为此问题构建搜索查询.

sha*_*ang 6

可以通过包含类型参数vw类定义来修复类型类.

class Edge e where
    endpoints :: e v w -> (v,v)
    weight :: e v w -> w
Run Code Online (Sandbox Code Playgroud)

现在e有那种* -> * -> *意思,它需要是一个类型,它有两个额外的类型参数,而这些参数,然后在使用endpointsweight实际联系起来的结果类型到边缘的类型.

但是,您需要调整您的ColoredEdge类型位,所以vw是最后两个参数,所以

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}
Run Code Online (Sandbox Code Playgroud)

现在您可以将实例定义为

instance Edge BasicEdge where
    endpoints = b_endpoints
    weight = b_weight

instance Edge (ColoredEdge c) where
    endpoints = c_endpoints
    weight = c_weight
Run Code Online (Sandbox Code Playgroud)

另一种选择是使用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
Run Code Online (Sandbox Code Playgroud)

但是,通常类型类不是这种多态的最佳解决方案.我只需在您的Edge类型中为任何其他数据添加一个额外参数,如

data Edge v w d = Edge { endpoints :: (v,v), weight :: w, edgeData :: d }
Run Code Online (Sandbox Code Playgroud)

现在,您可以将颜色d或包含多个数据字段的记录放入边缘,并仍以通用方式查询图形的形状.

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

type BasicEdge   v w   = Edge v w ()
type ColoredEdge v w c = Edge v w c
Run Code Online (Sandbox Code Playgroud)

  • 我开始写一个答案,我忙于描述fundeps和AT,我完全错过了明显的Haskell 2010解决方案.打的好! (2认同)