我正在尝试在幻像类型中乘以单元数组(来自dimensional
)而我遇到了函数依赖性问题。该问题的简化版本如下:
我有以下类型
data F (a:: [*]) = F String
其中字符串表示外语表达式和表示类型列表的幻像类型。
我可以做像
这样的事情x = F "x" :: F '[Double]
y = F "(1,3)" :: F '[Int, Int]
我设法通过创建Nums
类来实现这样的算术运算符,这是Num
的列表。
class Nums (a::[*])
instance Nums '[]
instance (Num a, Nums as) => Num (a ': as)
然后我可以做instanciate Num F
instance Nums as => F as where
(F a) * (F b) = F (a ++ "*" ++ b)
... etc ...
现在,我正在尝试使用物理单位做同样的事情。我可以用这种方式列出一种类型
import qualified Numeric.Units.Dimensional as Dim
data F (a::[*]) = F String
(!*!) :: (Num n, Dim.Mul a b c) => F '[Dim.Dimensional v a n]
-> F '[Dim.Dimensional v b n]
-> F '[Dim.Dimensional v c n]
(F a) !*! (F b) = F (a ++ "*" ++ b)
这似乎有用,我可以“乘以”不同单位的2'F,结果在校正单位。
显然,我想将此概括为任何列表,并使用与我称之为Nums
的{{1}}相同的技巧。
Muls
我收到class Muls a b c | a b -> c
instance '[] '[] '[]
instance (Num n, Mul a b c, Muls as bs cs)
=> Muls (Dim.Dimensional v a n ': as)
(Dim.Dimensional v b n ': bs)
(Dim.Dimensional v c n ': cs)
!*! :: (Muls as bs cs) => F as -> F bs -> F cs
(F a) !*! (F b) = F (a ++ "*" ++ b)
错误:
Illegal Instance declaration
如果我使用Illegal instance declaration for
‘Muls
(Dim.Dimensional v a n : as)
(Dim.Dimensional v b n : bs)
(Dim.Dimensional v c n : cs)’
The coverage condition fails in class ‘Muls’
for functional dependency: ‘a b -> c’
Reason: lhs types ‘Dim.Dimensional v a n : as’, ‘Dim.Dimensional
v b n
: bs’
do not jointly determine rhs type ‘Dim.Dimensional v c n : cs’
Using UndecidableInstances might help
In the instance declaration for
‘Muls (Dim.Dimensional v a n : as) (Dim.Dimensional v b n
: bs) (Dim.Dimensional v c n : cs)’
扩展名,它似乎确实有效。我的问题是,为什么我需要这个扩展,有没有办法可以避免它?
或者,我可能会使用UndecidableInstances
的类型系列版本来实现此功能。不幸的是,我需要自定义单位,并不清楚dimensional
是否支持用户定义的单位。
答案 0 :(得分:2)
默认情况下,Haskell要求实例选择是可判定的,即,尝试确定类型是否满足约束不会导致编译器中的无限循环。请考虑以下代码:
class A (a :: *)
class B (a :: *)
instance A a => B a
这显然是实例会导致无限循环(它不会这样做!)。即使每个其他实例本身都不会导致无限循环,添加此实例也可以。可能有一个严格的证据证明这一点,但我不知道。
UndecidableInstances
唯一能做的就是说“我保证我永远不会用导致无限循环的类型来调用我的函数,所以即使我的实例可以产生无限循环,我也要承担责任,确保不会发生“。
另一方面,形式的实例:
instance (C1 a1, C2 a2 ... Cn an) => C (T a1 a2 .. an)
永远不会产生无限循环,因为Haskell不允许无限类型,并且这个实例解包了一个构造函数,所以即使Ci
引用回C
,你最终也会得到一个类型具有0个类型参数的构造函数。
如果您编写的实例不可判定,则应该获取
test.hs:26:10:
Constraint is no smaller than the instance head
in the constraint: A a
(Use UndecidableInstances to permit this)
In the instance declaration for `B a'
我认为这是您在案例中应该看到的错误,并且显示您实际看到的错误应该被视为错误。