我试图定义一个非常简单的数据结构,假设将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)
其中toNum
将x
转换为正确的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