类型类实例具有更严格的签名 [英] Type class instance with more restrictive signature

查看:46
本文介绍了类型类实例具有更严格的签名的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比方说,我正在写一种数据类型来表示笛卡尔坐标系中的坐标.我想在该数据类型上定义函数,并使用Haskell类型检查来防止x轴上的数字与y轴上的数字混淆.

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

 数据X数据Ynewtype坐标轴=坐标Int64派生(显示)newX :: Int64->坐标XnewX =坐标newY :: Int64->Y坐标newY =坐标 

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

  slideByInt ::协调-->Int64->坐标aslideByInt(坐标x)y =坐标$ x + yslideByCoord ::协调->协调->坐标aslideByCoord(坐标x)(坐标y)=坐标(x + y) 

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

我的问题是:如何将 slideByInt slideByCoord 功能包装在一个类的后面,这样我就可以使用 slide 函数.编译:

  class将a滑动到幻灯片::坐标x->a->坐标x实例Slide Int64其中滑动(坐标x)y =坐标(x + y)实例幻灯片(坐标x),其中幻灯片(x坐标)(y坐标)=坐标(x + y) 

但是它的类型安全性不及独立函数: slide(newX 1)(newY 1)不应键入check!从某种意义上说,如何解决这个问题,如何使两个坐标的实例比实际的宽松?

我尝试了很多扩展(InstanceSigs,FunctionalDependency,类型约束...),但是没有任何编译,很难分辨出这是完全错误的方式,还是我只需要稍微调整一下代码./p>

谢谢...

解决方案

考虑此类声明的内容:

  class将a滑动到幻灯片::坐标x->a->坐标x 

对于 any 类型 x Slide 的一个实例保证给出给定的 Coordinate x a ,它将为您返回坐标x .正确,这是您的问题.您不一直想要任何 x .

我认为最简单的方法是使用坐标类型的第二个类参数:

  {-#语言MultiParamTypeClasses#-}{-#语言FlexibleInstances#-}幻灯片x在哪里幻灯片::坐标x->a->坐标x实例Slide X Int64,其中幻灯片= slideByInt实例Slide Y Int64其中幻灯片= slideByInt实例幻灯片X(坐标X),其中幻灯片= slideByCoord实例幻灯片Y(坐标Y),其中幻灯片= slideByCoord 

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

  {-#语言类型家庭#-}实例(a〜b)=>将a(坐标b)滑到幻灯片= slideByCoord 

对于它的价值,我想避免以这种方式使用类型类.我认为重载功能带来的即时便利不值得一试,而且长期的维护负担也不值得.但这只是我的意见.

Let's say I'm writing a data type to represent a coordinate in a cartesian coordinate system. I'd like to define functions on that data type and use Haskell type checking to prevent mixing up numbers that lie on x axis with the numbers on the y axis.

Here's the data type definition, with a phantom type that tracks the coordinate axis and two functions to construct the values:

data X
data Y

newtype Coordinate axis = Coordinate Int64 deriving (Show)

newX :: Int64 -> Coordinate X
newX = Coordinate

newY :: Int64 -> Coordinate Y
newY = Coordinate

Let's define a sliding function that slides the coordinate, either by Int value or another Coordinate value. In the first case the coordinate should keep its axis and in the second case both arguments should have the same axis:

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)

This all works great and it prevents me from confusing X and Y axis in functions that manipulate Coordinates.

My question is: how would I wrap slideByInt and slideByCoord functionality behind a class, so that I can have just with the slide function. This compiles:

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)

but it's not as type safe as the standalone functions: slide (newX 1) (newY 1) should not type check! How would one go about fixing this, in a sense, how can I make the instance for two Coordinates less permissive than it is?

I've tried with a bunch of extensions (InstanceSigs, FunctionalDependencies, type constraints...) but nothing compiles and it's hard to tell if that's the wrong way completely or I just have to tweak my code a little bit.

Thanks...

解决方案

Consider what this class declaration is stating:

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

for any type x, an instance of Slide promises that given a Coordinate x and an a, it will give you back a Coordinate x. Right there is your problem. You don't want any x all the time.

I think the easiest way to achieve what you want is with a second type class parameter for the coordinate type:

{-# 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

The last two instances can actually be replaced with this more general instance:

{-# LANGUAGE TypeFamilies #-}

instance (a ~ b) => Slide a (Coordinate b) where
  slide = slideByCoord

For what it's worth, I like to avoid using typeclasses in this manner. I don't think the immediate convenience of overloaded functions is worth the boilerplate and long-term maintenance burden. But that's just my opinion.

这篇关于类型类实例具有更严格的签名的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆