我正在开发一个专门的数值数据处理库,我遇到了一个错误,我无法弄清楚如何修复.我认为首先展示一个例子然后解释我的问题会更容易.我也为这些陌生的名字道歉,我不得不为法律目的进行混淆.
{-# 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
Run Code Online (Sandbox Code Playgroud)
如果我尝试编译它,我得到错误
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'
Run Code Online (Sandbox Code Playgroud)
因为编译器无法确定MyNum要使用哪个特定实例MyList.它的工作原理是myAdd因为返回类型MyNum很容易导出,但它无法解决myLessThan.我想使用这个类型类,以便我可以轻松地在整个过程中添加细粒度的错误处理,因为我的实际代码具有+, - ,*,/,<,<=,>和> =的等价物,我想要做一个实例MyNum Double MyList MyList,MyNum MyList MyList MyList以及类似的人NamedList.除非有更简单的方法,否则我可以拥有多态交换运算符.
但是,我无法弄清楚要myLessThan为第二个实例添加什么类型的签名,以便它可以知道要使用的实例.我知道一种解决方案是将算术运算符和比较运算符分成两个独立的类型类,但我想尽可能避免这样做.
jbe*_*man 11
您可以使用函数依赖项来指定" ret由aand和唯一确定b".
...
{-# LANGUAGE FunctionalDependencies #-}
...
class MyNum a b ret | a b -> ret where
...
Run Code Online (Sandbox Code Playgroud)
这让typechecker知道它可以选择正确的实例定义,只知道你的参数a和b来自:
myLessThan (NamedList n l) x = myLessThan l x
Run Code Online (Sandbox Code Playgroud)
编译器现在会抱怨,如果你用相同的定义一个额外的实例a和b而不同ret,如
instance MyNum MyList Double SomeOtherType where
Run Code Online (Sandbox Code Playgroud)