我想为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编写它的正确方法是什么?
w 这个众所周知的问题是我的宠儿。正确的解决方案是使XY和Point类不用于参数类型。标量参数成为关联的类型同义词,一切都容易实现:
{-# 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)
注意实际上,您甚至可以考虑简化此操作以始终使用相同的组件类型,因为您可能永远不需要其他任何东西:
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
使用非参数形式,您现在可以轻松地将数字类型约束添加到需要的确切位置,即在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)
出于某种原因†,但是大多数人更喜欢使几何实体的类成为标量类型以外的参数,这不仅是完全不必要的,而且也是非几何的,因为强调合适的几何体并不依赖于实际的基础分解。
linear库。他本应该了解得更多,尤其是因为以正确的方式操作的Conal Elliott的vector-space图书馆已经存在了很长时间。