递归函数依赖不起作用 [英] Recursive functional dependency not working

查看:167
本文介绍了递归函数依赖不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图乘以幻象类型的单位数组(从),并且遇到函数依赖性问题。这个问题的简化版本如下:

我有以下类型:

  data F(a :: [*])= F String 

一个外语表达式和一个幻象类型表示一个类型列表。



我可以做一些事情,比如

 x = Fx:: F'[Double] 
y = F(1,3):: F'[Int,Int]

我通过创建一个 Nums 类来设法实现这样的算术运算符这是 Num 的列表。

  class Nums(a :: [*])
实例Nums'[]
实例(Num a,Nums as)=> Num(a':as)

然后我可以执行instanciate Num F

  instance Nums as => F为其中
(F a)*(F b)= F(a ++*++ b)
...等...

现在,我正在尝试使用物理单位来做同样的事情。我可以用这种方式的一种类型的列表来做到这一点

 将合格的Numeric.Units.Dimensional导入为Dim 

data F(a :: [*])= F String
(!*!)::(Num n,Dim.Mul abc)=> 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 abc | 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)

code>非法实例声明错误:

  
' Muls
(Dim.Dimensional van:as)
(Dim.Dimensional vbn:bs)
(Dim.Dimensional vcn:cs)'
'Muls' '
表示函数依赖:'ab - > c'
原因:lhs类型'Dim.Dimensional van:as','Dim.Dimensional
vbn
:bs'
不共同确定rhs类型'Dim.Dimensional vcn: cs'
使用UndecidableInstances可能有助于

'Muls(Dim.Dimensional van:as)的实例声明中(Dim.Dimensional vbn
:bs)(Dim.Dimensional vcn :cs)'

如果我使用 UndecidableInstances 扩展,它似乎确实工作。我的问题是,为什么我需要这个扩展,有没有办法避免它?



另外,我可能可以使用类型系列版本的。不幸的是,我需要自定义单位,并且不清楚 dimensional-tf 是否支持用户定义的单位。

解决方案

默认情况下,Haskell要求实例选择是可判定的,即试图判定一个类型是否满足约束条件不会导致编译器中的无限循环。考虑以下代码:

  class A(a :: *)
class B(a :: *)
实例A a => B a

这个明显的实例可能导致无限循环(它赢了'这样做必然!)。即使每个其他实例本身都不会导致无限循环,但添加此实例也是可以的。这个地方可能有严格的证据,但我不知道。



UndecidableInstances 确实如此:我保证我永远不会用导致无限循环的类型调用我的函数,所以即使我的实例可能会产生无限循环,但我承担责任确保不会发生。



另一方面,形式的实例:

 实例(C1 a1,C2 a2 ... Cn an)=> C(T a1 a2 .. an)

永远不会产生无限循环,因为Haskell不会允许无限类型,并且这个实例解包一个构造函数,所以即使 Ci 返回到 C ,您将最终得到到一个具有0个类型参数的类型构造函数。



如果你写了一个不可判定的实例,你应该得到

 test.hs:26:10:
约束不小于约束中的实例头部
:A a
(使用UndecidableInstances来允许)
在`B a'的实例声明中

我认为这是你应该犯的错误看你的情况,并显示你实际看到的错误应该被视为一个错误。

I'm trying to multiply array of units (from dimensional) in a phantom type and I'm having trouble with functional dependencies. A simplified version of the problem is the following :

I have the following type

data F (a:: [*]) = F String

Where the string represent an expression in a foreign language and a phantom type representing a list of types.

I can do things like

x = F "x" :: F '[Double]
y = F "(1,3)" :: F '[Int, Int]

I managed to implements arithmetics operators like this by creating a Nums class which is a list of Num.

class Nums (a::[*])
instance Nums '[]
instance (Num a, Nums as) => Num (a ': as)

Then I can do instanciate Num F

instance Nums as => F as where
   (F a) * (F b) = F (a ++ "*" ++ b)
   ... etc ...

Now, I'm trying to do the same using physical units. I can do it with a list of one type this way

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)

That's seem to work, I can "multiply" 2 'F's of different units, and the result is in the corret unit. Obviously, I want to generalize this to any list and use the same tricks as Nums which I call 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)

I got an Illegal Instance declaration error :

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)’

If I use the UndecidableInstances extension, it seems to work indeed. My question, is why do I need this extension and is there a way I could avoid it ?

Alternatively, I probably could get this working using the type family version of dimensional. Unfortunatly, I need custom units and it's not clear if dimensional-tf supports user defined units.

解决方案

By default Haskell requires that instance selection be decidable, ie, that an attempt to decide if an type satisfy a constraint cannot cause an infinite loop in the compiler. Consider the following code:

class A (a :: *)
class B (a :: *) 
instance A a => B a 

This obviously instance could cause an infinite loop (it won't do so necessarily!). Even if every other instance by itself can't cause an infinite loop, the addition of this instance can. There is probably a rigorous proof of this somewhere but I don't know it.

The only thing UndecidableInstances does is say "I promise I will never call my functions with types which cause infinite loops, so even though my instances can produce infinite loops, I accept responsibility for making sure that doesn't happen."

On the other hand, instances of the form:

instance (C1 a1, C2 a2 ... Cn an) => C (T a1 a2 .. an)

Will never produce an infinite loop, since Haskell doesn't allow infinite types, and this instance unpacks a single constructor, so even if Ci refers back to C you will eventually get to a type constructor which has 0 type parameters.

If you write an instance which is not decidable, you should get

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'

I think this is the error you should see in your case and that displaying the error you actually see should be considered a bug.

这篇关于递归函数依赖不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆