Haskell:约束

时间:2017-06-21 10:33:19

标签: haskell polymorphism typeclass parametric-polymorphism

我创建了一些函数来让2D几何体感到舒适。 在此示例中,我使用Geom2D from CubicBezier package

我的计划的完整代码:https://gist.github.com/nskeip/3784d651ac646a67c5f246f048949af4

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
import Geom2D

left :: (Num a) => Point a -> a -> Point a
left (Point x y) n = Point (x - n) y

right :: (Num a) => Point a -> a -> Point a
right (Point x y) n = Point (x + n) y

up :: (Num a) => Point a -> a -> Point a
up (Point x y) n = Point x (y - n)

down :: (Num a) => Point a -> a -> Point a
down (Point x y) n = Point x (y + n)

他们的工作方式如下:

> (Point 0 0) `up` 10
Point 0.0 -10.0

Point的定义如下:

data Point a = Point {
  pointX :: !a,
  pointY :: !a
  } deriving (Eq, Ord, Functor, Foldable, Traversable)

一切都很好,直到我想:"嘿,让这些函数(实际上,运算符)与Line之类的问题一起工作会很好 - 不仅Point&#34 ;

所以我宣布了一个班级(不考虑leftright以保持简单):

class Num n => Moving p n where
  up'    :: n -> p -> p
  down'  :: n -> p -> p

  up'    n = down'  (-n)
  down'  n = up'    (-n)

Moving数据类型Point a的实例:

instance Num a => Moving (Point a) a where
  up' n (Point x y) = Point x (y - n)

但是当我尝试使用它时,我收到了一个错误:

✗ ghci ./uno.hs 
GHCi, version 7.10.3: http://www.haskell.org/ghc/  :? for help
[1 of 1] Compiling Main             ( uno.hs, interpreted )
Ok, modules loaded: Main.
*Main> let p = Point { pointX = 0, pointY = 0 } 
*Main> up' 10 p

<interactive>:3:1:
    Non type-variable argument in the constraint: Moving (Point a) n
    (Use FlexibleContexts to permit this)
    When checking that ‘it’ has the inferred type
      it :: forall n a. (Num a, Moving (Point a) n) => Point a

让我感到困惑的是:我把FlexibleContexts pragma放到头部的pragma列表中,但是ghcu仍然建议我把它包含在内。

如何修复我的类/实例以使参数多态性有效? :)

1 个答案:

答案 0 :(得分:1)

  

让我感到困惑的是:我把FlexibleContexts pragma放到头部的pragma列表中,但是ghcu仍然建议我把它包含在内。

这仅启用模块本身的扩展。要在GHCi中编写此代码,您需要在GHCi中启用扩展名::set -XFlexibleContexts

但这只是问题的一部分。您的班级p似乎应该确定n:您只能Point a向上和向下移动a,对吧?但是就目前而言,没有什么能阻止你定义更多的Moving (Point a) SomeOtherType实例,并且编译器并不认为你不会。因此,推断类型中的an完全不相关,您希望它们相同。这可以通过添加FunctionalDependencies extension并将类声明更改为

来修复
class Num n => Moving p n | p -> n where

这意味着不能存在具有相同p和不同n的实例。

我认为这足以让它发挥作用。代码仍然不确定,因为它允许任何数字a,但默认规则将选择Integer