键入具有更严格签名的类实例

kme*_*lvn 2 haskell typeclass

假设我正在编写一种数据类型来表示笛卡尔坐标系中的坐标.我想在该数据类型上定义函数并使用Haskell类型检查来防止将位于x轴上的数字与y轴上的数字混合.

这是数据类型定义,具有跟踪坐标轴的幻像类型和构造值的两个函数:

data X
data Y

newtype Coordinate axis = Coordinate Int64 deriving (Show)

newX :: Int64 -> Coordinate X
newX = Coordinate

newY :: Int64 -> Coordinate Y
newY = Coordinate
Run Code Online (Sandbox Code Playgroud)

让我们定义一个滑动函数,通过Int值或另一个Coordinate值滑动坐标.在第一种情况下,坐标应保持其轴,在第二种情况下,两个参数应具有相同的轴:

slideByInt :: Coordinate a -> Int64 -> Coordinate a
slideByInt (Coordinate x) y = Coordinate $ x + y

slideByCoord :: Coordinate a -> Coordinate a -> Coordinate a
slideByCoord (Coordinate x) (Coordinate y) = Coordinate (x + y)
Run Code Online (Sandbox Code Playgroud)

这一切都很有效,它可以防止我在操纵坐标的函数中混淆X轴和Y轴.

我的问题是:如何在类后面进行包装slideByIntslideByCoord功能,以便我可以使用该slide函数.这编译:

class Slide a where
  slide :: Coordinate x -> a -> Coordinate x

instance Slide Int64 where
  slide (Coordinate x) y = Coordinate (x + y)

instance Slide (Coordinate x) where
  slide (Coordinate x) (Coordinate y) = Coordinate (x + y)
Run Code Online (Sandbox Code Playgroud)

但它不像独立功能那样安全:slide (newX 1) (newY 1)不应该打字检查!从某种意义上说,如何解决这个问题,如何使两个坐标的实例不那么宽松?

我尝试过一堆扩展(InstanceSigs,FunctionalDependencies,类型约束......),但没有编译,很难判断这是完全错误的方式还是我只需稍微调整一下我的代码.

谢谢...

use*_*560 5

考虑一下这个类声明的内容:

class Slide a where
  slide :: Coordinate x -> a -> Coordinate x
Run Code Online (Sandbox Code Playgroud)

对于任何类型x,Slide给出a Coordinate x和a 的promises 实例a,它会给你一个Coordinate x.对,那是你的问题.你不要一直想要任何东西 x.

我认为实现所需内容的最简单方法是使用坐标类型的第二个类类参数:

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}

class Slide x a where
  slide :: Coordinate x -> a -> Coordinate x

instance Slide X Int64 where
  slide = slideByInt

instance Slide Y Int64 where
  slide = slideByInt

instance Slide X (Coordinate X) where
  slide = slideByCoord

instance Slide Y (Coordinate Y) where
  slide = slideByCoord
Run Code Online (Sandbox Code Playgroud)

最后两个实例实际上可以用这个更通用的实例替换:

{-# LANGUAGE TypeFamilies #-}

instance (a ~ b) => Slide a (Coordinate b) where
  slide = slideByCoord
Run Code Online (Sandbox Code Playgroud)

对于它的价值,我喜欢避免以这种方式使用类型类.我不认为超载功能的直接便利是值得的样板和长期维护负担.但那只是我的个人意见.