具有上下文的Haskell数据类型

Ego*_*kin 2 haskell

我想为Vertex编写基本实现。

data Point a = Point a a

class XY c where

    x :: c a -> a

    y :: c a -> a

class XY c => Vertex c where

    translate :: c a -> c a -> c a

    scale :: a -> c a -> c a

    rotate :: a -> c a -> c a

instance XY Point where

    x (Point first second) = first

    y (Point first second) = second

instance Vertex Point where

    translate xy1 xy2 = Point (x xy1 + x xy2) (y xy1 + y xy2)

    scale a xy = Point ((*a) $ x xy) ((*a) $ y xy)

    rotate a xy = Point (x * cosA - y * sinA) (x * sinA + y * cosA) where
                    cosA = cos a
                    sinA = sin a
Run Code Online (Sandbox Code Playgroud)

我必须instance在Point type参数中使用Floating typeclass的实现来创建Typeclass Vertex。如果我实现它像instance (Floating a) => Vertex Point a where我得到:

 Expected kind ‘* -> Constraint’,
        but ‘Vertex Point’ has kind ‘Constraint’
Run Code Online (Sandbox Code Playgroud)

用Haskell编写它的正确方法是什么?

lef*_*out 5

w 这个众所周知的问题是我的宠儿。正确的解决方案是使XYPoint用于参数类型。标量参数成为关联的类型同义词,一切都容易实现:

{-# LANGUAGE TypeFamilies #-}

class XY p where
  type Component p :: *
  x :: p -> Component p
  y :: p -> Component p

class XY p => Vertex p where
  translate :: p -> p -> p
  scale :: Component p -> p -> p
  rotate :: Component p -> p -> p
Run Code Online (Sandbox Code Playgroud)

注意实际上,您甚至可以考虑简化此操作以始终使用相同的组件类型,因为您可能永远不需要其他任何东西:

class XY p where
  x :: p -> Double
  y :: p -> Double
class XY p => Vertex p where
  translate :: p -> p -> p
  scale :: Double -> p -> p
  rotate :: Double -> p -> p
Run Code Online (Sandbox Code Playgroud)

使用非参数形式,您现在可以轻松地将数字类型约束添加到需要的确切位置,即在instance Vertex Point实例中:

instance XY (Point a) where
  type Component (Point a) = a
  x (Point xc _) = xc
  y (Point _ yc) = yc

instance Floating a => Vertex (Point a) where
  translate xy1 xy2 = Point (x xy1 + x xy2) (y xy1 + y xy2)
  scale a xy = Point ((*a) $ x xy) ((*a) $ y xy)
  rotate a xy = Point (x * cosA - y * sinA) (x * sinA + y * cosA)
   where cosA = cos a
         sinA = sin a
Run Code Online (Sandbox Code Playgroud)

出于某种原因,但是大多数人更喜欢使几何实体的类成为标量类型以外的参数,这不仅是完全不必要的,而且也是非几何的,因为强调合适的几何体并不依赖于实际的基础分解。


其实我相当肯定的原因是什么:爱德华Kmett的决定使用参数化类型linear。他本应该了解得更多,尤其是因为以正确的方式操作的Conal Elliott的vector-space图书馆已经存在了很长时间。