关于haskell:在实例声明中键入约束,但不在类中键入约束

Type constraints in instance declaration but not class

我想创建一个具有类型约束的函数定义的实例,但不想将类型约束添加到类中。

1
2
3
4
5
6
7
8
9
class Foo a where
  f :: a b -> b

instance Foo Maybe where
  f = fMaybe

fMaybe :: (Num a) => Maybe a -> a
fMaybe (Just i) = i+i
fMaybe _ = 0

如何指定这是包含NumsMaybe的实例?

这项工作:

1
2
3
4
5
6
7
8
9
10
{-# LANGUAGE MultiParamTypeClasses #-}
class Foo a b where
  f :: a b -> b

instance Foo Maybe Int where
  f = fMaybe

fMaybe :: (Num a) => Maybe a -> a
fMaybe (Just i) = i+i
fMaybe _ = 0

但我不想为每种类型的Num声明实例

我试过这个:

1
2
3
4
5
6
7
8
9
class Foo a where
  f :: a -> b

instance (Num b) => Foo (Maybe b) where
  f = fMaybe

fMaybe :: (Num a) => Maybe a -> a
fMaybe (Just i) = i+i
fMaybe _ = 0

但我得到一个错误:

1
2
3
4
5
6
7
8
9
10
11
Couldn't match type ‘b’ with ‘b1’
  ‘b’ is a rigid type variable bound by
      the instance declaration at Test.hs:31:10
  ‘b1’ is a rigid type variable bound by
       the type signature for f :: Maybe b -> b1 at Test.hs:32:3
Expected type: Maybe b -> b1
  Actual type: Maybe b1 -> b1
Relevant bindings include
  f :: Maybe b -> b1 (bound at Test.hs:32:3)
In the expression: fMaybe
In an equation for ‘f’: f = fMaybe


不更改类或类型是不可能的。

  • MultiParamTypeClasses方法实际上并不要求您为所有Num类型编写单独的实例—以下工作:

    1
    2
    3
    4
    5
    class Foo a b where
      f :: a b -> b

    instance (Num b) => Foo Maybe b where
      f = fMaybe

    不过,我认为这不是一个特别好的方法。

  • 您可以使用ConstraintKinds允许每个实例有选择地约束包含的类型。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    {-# LANGAUGE TypeFamilies, ConstraintKinds #-}

    import GHC.Exts (Constraint)

    class Foo a where
      type FooCstrt a b :: Constraint
      type FooCstrt a b = () -- default to unconstrained
      f :: FooCstrt a b => a b -> b

    instance Foo Maybe where
      type FooCstrt Maybe b = Num b
      f = fMaybe

  • 您可以切换到最初只允许包含Num类型的类型。

    1
    2
    3
    4
    5
    {-# LANGUAGE GADTs #-}

    data NMaybe where
      NJust :: Num b => b -> NMaybe b
      NNothing :: Num b => NMaybe b

    然后,

    1
    2
    3
    4
    5
    6
    class Foo a where
      f :: a b -> b

    instance Foo NMaybe where
      f (NJust i) = i+1
      f (NNothing) = 0