我正在开发一个专门的数值数据处理库,但遇到了一个我无法解决的错误。我认为先展示一个例子然后解释我的问题会更容易。我也为奇怪的名字道歉,出于法律目的我不得不混淆。
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
data MyError = MyError String deriving (Eq, Show)
data MyList = MyList [Double] deriving (Eq, Show)
data NamedList = NamedList String MyList deriving (Eq, Show)
class MyNum a b ret where
myAdd :: a -> b -> Either MyError ret
myLessThan :: a -> b -> Either MyError Bool
instance MyNum MyList Double MyList where
myAdd (MyList xs) x = Right $ MyList $ map (+x) xs
myLessThan (MyList xs) x = Right $ all (< x) xs
instance MyNum NamedList Double NamedList where
myAdd (NamedList n l) x = fmap (NamedList n) $ myAdd l x
myLessThan (NamedList n l) x = myLessThan l x
如果我尝试编译它,我会收到错误
No instance for (MyNum MyList Double ret0)
arising from a use of `myLessThan'
The type variable `ret0' is ambiguous
Possible fix: add a type signature that fixes these type variable(s)
Note: there is a potential instance available:
instance MyNum MyList Double MyList
-- Defined at testing_instances.hs:13:10
Possible fix:
add an instance declaration for (MyNum MyList Double ret0)
In the expression: myLessThan l x
In an equation for `myLessThan':
myLessThan (NamedList n l) x = myLessThan l x
In the instance declaration for `MyNum NamedList Double NamedList'
因为编译器无法确定MyNum
用于MyList
. 它之所以有效,是myAdd
因为 for 的返回类型MyNum
很容易派生,但它无法弄清楚 for myLessThan
。我想使用这个类型类,以便我可以轻松地在整个过程中添加细粒度的错误处理,并且因为我的实际代码具有 +、-、*、/、<、<=、> 和 >= 的等价物,并且我想要为MyNum Double MyList MyList
,MyNum MyList MyList MyList
和类似的实例创建NamedList
. 除非有更简单的方法可以做到这一点,否则我可以拥有多态交换运算符。
但是,我无法弄清楚要myLessThan
为第二个实例添加什么类型签名,以便它知道要使用哪个实例。我知道一种解决方案是将算术和比较运算符拆分为两个单独的类型类,但我想尽可能避免这样做。