haskell – 递归函数依赖不起作用

我试图在幻像类型中乘以单位数组(来自维度)而我遇到了函数依赖性问题.问题的简化版本如下:

我有以下类型

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'

我认为这是您在案例中应该看到的错误,并且显示您实际看到的错误应该被视为错误.

点赞