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

  • 本文关键字:类型 语句 实例 存在 haskell
  • 更新时间 :
  • 英文 :


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

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.

我理解它说Finite x中的x不一定与签名fromNum :: Num a => a -> k中的a具有相同的类型。我该如何指定呢?

一个简单的解决方案(即不诉诸花哨的类型系统技巧)是使您的类使用类型* -> *而不是*。在实践中,这只意味着您将其定义为:

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类型的约束已经足够好了。

问题是您试图从任何Num定义fromNum到任何其他Num,这是不可能的。你会喜欢像

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

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

你也可以很容易地为NumContainer定义一个Num实例,然后你可以实现fromInteger作为

frommInteger a = Finite (fromInteger a)

转换现在是可能的,因为您不是从任意的Num开始,而是从Integer开始。

也请不要,目前没有一个Infinite你的类型是同构的Maybe

为什么需要一个类型类?通常需要使用类型类来根据类型重载函数。所以除非你打算使用不同的容器,否则你可能不需要类型类,只需要

 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

最新更新