当存在类型类限制时,如何在实例语句中指定类型?

时间:2015-08-31 04:41:50

标签: haskell

我试图定义一个非常简单的数据结构,假设将Infinity元素添加到Num下的任何类型。我还将它放在已定义的类NumContainer下,该类具有使用常规fromNum构造NumWithInf的方法Num。代码非常简单。

data NumWithInf a = Infinity | Finite a deriving Show

class NumContainer k where
    fromNum :: Num a => a -> k

instance Num a => NumContainer (NumWithInf a) where
    fromNum x = Finite x

然而,当我运行它时,GHCI给了我以下错误:

hw.hs:7:24:
    Could not deduce (a ~ a1)
    from the context (Num a)
      bound by the instance declaration at hw.hs:6:10-45
    or from (Num a1)
      bound by the type signature for
                 fromNum :: Num a1 => a1 -> NumWithInf a
      at hw.hs:7:5-24
      `a' is a rigid type variable bound by
          the instance declaration at hw.hs:6:10
      `a1' is a rigid type variable bound by
           the type signature for fromNum :: Num a1 => a1 -> NumWithInf a
           at hw.hs:7:5
    In the first argument of `Finite', namely `x'
    In the expression: Finite x
    In an equation for `fromNum': fromNum x = Finite x
Failed, modules loaded: none.

据我所知,它表示x中的Finite x与签名a中的fromNum :: Num a => a -> k的类型不一定相同。我该如何指定?

3 个答案:

答案 0 :(得分:5)

一个简单的解决方案(即不使用花哨类型系统欺骗的解决方案)使您的班级使用类型* -> *而不是*。实际上,这只是意味着你将其定义为:

class NumContainer k where
    fromNum :: Num a => a -> k a -- Result type parametrised on `a` 

然后该实例变为:

instance NumContainer NumWithInf where
    fromNum x = Finite x

请注意,没有地方可以在实例中放置Num约束。无论如何这都是不必要的 - fromNum类型中的约束已经足够好了。

答案 1 :(得分:2)

问题是您尝试将fromNum从任意Num定义到任何其他Num,这是不可能的。你想要像

这样的东西
fromNum x = Finite (toNum x)

其中toNumx转换为正确的Num。在一般情况下这是不可能的。但是,您的容器实际上是一个应用仿函数,您的函数fromNum(签名较窄a -> k a)只是pure

此外,您可以轻松定义Num的{​​{1}}个实例,然后您可以将NumContainer实现为

fromInteger

现在可以进行转换,因为您不是从任意frommInteger a = Finite (fromInteger a) 开始,而是从Num开始。

另请注意,目前只有一个Integer您的类型与Infinite同构。

答案 2 :(得分:0)

为什么需要类型类?通常需要类型类来根据类型重载函数。因此,除非您计划使用不同的容器,否则您可能不需要类型类,而只能执行

 fromNum :: Num a => a -> NumWithInf a
 fromNum = Finite

但是,您的问题“当存在类型类限制时,如何在实例语句中指定类型?”您可以使用MultiParamTypeClasse执行此操作,并将a作为类型类的参数传递。

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances #-}

data NumWithInf a = Infinity | Finite a deriving Show

class NumContainer a k  where
       fromNum :: Num a => a -> k

instance Num a => NumContainer a (NumWithInf a) where
       fromNum x = Finite  x