我试图在幻像类型中乘以单位数组(来自维度)而我遇到了函数依赖性问题.问题的简化版本如下:
我有以下类型
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)
然后我可以做实例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相同的技巧,我称之为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 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扩展,它似乎确实有效.我的问题是,为什么我需要这个扩展,有没有办法可以避免它?
或者,我可能会使用维度的类型族版本来实现这一点.不幸的是,我需要自定义单位,并且不清楚维度-tf是否支持用户定义的单位.
最佳答案 默认情况下,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'
我认为这是您在案例中应该看到的错误,并且显示您实际看到的错误应该被视为错误.