【发布时间】:2017-02-02 03:07:36
【问题描述】:
假设我正在编写一个数据类型来表示笛卡尔坐标系中的坐标。我想在该数据类型上定义函数并使用 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
让我们定义一个滑动坐标的滑动函数,可以是 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)
这一切都很好,它可以防止我在操纵坐标的函数中混淆 X 和 Y 轴。
我的问题是:如何将 slideByInt 和 slideByCoord 功能包装在一个类后面,这样我就可以使用 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)
但它不像独立函数那样类型安全:slide (newX 1) (newY 1) 不应该类型检查!从某种意义上说,如何解决这个问题,我怎样才能使两个坐标的实例比它更宽松?
我尝试了一堆扩展(InstanceSigs、FunctionalDependencies、类型约束......),但没有任何编译,很难判断这是否完全是错误的方式,或者我只需要稍微调整我的代码。
谢谢...
【问题讨论】:
-
这是一个关于 typeclass 诡计的有趣问题。但是,在您的具体用例中,您可能需要考虑放弃
Slide类,给Coordinate x一个Num实例并编写newX 2 + newX 3,甚至是2 + 3 :: Coordinate X。 -
@duplode 我正在翻译一些 Python 代码并希望保留相同的 API,但在顶部添加一些类型安全性。现在我看到了答案,实际上看起来并没有那么棘手(与我见过的其他一些事情相比:>)。