【问题标题】:Recursive functional dependency not working递归函数依赖不起作用
【发布时间】:2014-10-15 10:31:27
【问题描述】:

我正在尝试以幻象类型乘以单元数组(来自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)

那我可以实例化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 错误:

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 的类型系列版本来完成这项工作。不幸的是,我需要自定义单位,不清楚dimensional-tf 是否支持用户定义的单位。

【问题讨论】:

    标签: haskell ghc functional-dependencies


    【解决方案1】:

    默认情况下,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'
    

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

    【讨论】:

    • 为什么“明显”会导致死循环?
    • @mb14 你同意instance C a => C a 可以产生无限循环的证明是微不足道的吗?在上述情况下,同样的原理也在起作用。由于类型类实例的全域是开放的,因此 instance A a => B a 中的约束 A a 可能 在扩展后最终包含 B a。所以A a 可能等于C1 a, C2 a, .. Cn a, B a。这将使实例减少到instance (C1 a, C2 a, .. Cn a, B a) => B a,这是微不足道的重叠。
    • 好的,或者我想我也可以instance B a => A a
    猜你喜欢
    • 2018-07-14
    • 2018-04-02
    • 2014-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-20
    • 2012-12-05
    相关资源
    最近更新 更多