约束追踪技术


322

这是一种情况:我已经编写了一些带有类型签名的代码,GHC抱怨无法为x x和y推导某些代码y。通常,您可以将GHC扔掉,然后将同构添加到功能约束中,但这是一个坏主意,原因有以下几个:

  1. 它不强调理解代码。
  2. 您最终可以得到5个约束,而其中一个约束就足够了(例如,如果5个约束被一个更具体的约束隐含)
  3. 如果您做错了某事或GHC无助,您可能会受到虚假约束的限制

我只花了几个小时与案例3进行斗争。我正在玩syntactic-2.0,并且试图定义一个与域无关的版本share,类似于中定义的版本NanoFeldspar.hs

我有这个:

{-# LANGUAGE GADTs, FlexibleContexts, TypeOperators #-}
import Data.Syntactic

-- Based on NanoFeldspar.hs
data Let a where
    Let :: Let (a :-> (a -> b) :-> Full b)

share :: (Let :<: sup,
          Domain a ~ sup,
          Domain b ~ sup,
          SyntacticN (a -> (a -> b) -> b) fi) 
      => a -> (a -> b) -> a
share = sugarSym Let

和GHC could not deduce (Internal a) ~ (Internal b),这当然不是我想要的。因此,要么我写了一些我不想写的代码(需要约束),要么GHC由于我写的其他一些约束而想要那个约束。

事实证明,我需要添加(Syntactic a, Syntactic b, Syntactic (a->b))到约束列表,但都没有暗示(Internal a) ~ (Internal b)。我基本上偶然发现了正确的约束;我仍然没有系统的方法来找到它们。

我的问题是:

  1. GHC为什么提出这一限制?句法上没有任何约束Internal a ~ Internal b,那么GHC是从哪里提取约束的呢?
  2. 通常,可以使用哪些技术来追踪GHC认为需要的约束的起源?即使对于我可以发现自己的约束,我的方法本质上还是通过物理地写下递归约束来强行逼迫违规路径。这种方法基本上是在无穷无尽的约束条件下进行的,是我所能想到的效率最低的方法。

21
在类型级别的调试器上已经进行了一些讨论,但是普遍的共识似乎表明类型检查器的内部逻辑将无济于事://目前,Haskell的约束求解器是一种糟糕的不透明逻辑语言:)
Daniel Gratzer

12
@jozefg您是否有该讨论的链接?
crockeea 2014年

36
通常,完全删除类型签名有助于让ghci告诉您它认为签名应该是什么。
Tobias Brandt 2014年

12
不知何故ab被绑定-在上下文之外查看类型签名- a -> (a -> b) -> a而不是a -> (a -> b) -> b。也许就是这样吗?使用约束求解器,它们可以在任何地方影响传递等式,但是错误通常会显示一个位置,该位置“接近”诱导约束的位置。@jozefg太酷了-也许用标签或其他东西注释约束,以显示约束的来源?:s
Athan Clark

Answers:


6

首先,您的函数类型错误;我很确定应该(没有上下文)a -> (a -> b) -> b。GHC 7.10在指出这一点上会更有帮助,因为在您的原始代码中,它抱怨缺少约束 Internal (a -> b) ~ (Internal a -> Internal a)。修正了share类型之后,GHC 7.10仍有助于指导我们:

  1. Could not deduce (Internal (a -> b) ~ (Internal a -> Internal b))

  2. 添加以上内容后,我们得到 Could not deduce (sup ~ Domain (a -> b))

  3. 补充说,之后,我们得到的Could not deduce (Syntactic a)Could not deduce (Syntactic b)Could not deduce (Syntactic (a -> b))

  4. 在添加了这三个之后,它最终进行类型检查。所以最终

    share :: (Let :<: sup,
              Domain a ~ sup,
              Domain b ~ sup,
              Domain (a -> b) ~ sup,
              Internal (a -> b) ~ (Internal a -> Internal b),
              Syntactic a, Syntactic b, Syntactic (a -> b),
              SyntacticN (a -> (a -> b) -> b) fi)
          => a -> (a -> b) -> b
    share = sugarSym Let

因此,我想说,GHC在领导我们方面并非没有用。

至于你提到的跟踪问题,其中GHC从获得其约束的要求,你可以尝试GHC的调试标志,特别是-ddump-tc-trace,然后通过生成的日志看,看看那里Internal (a -> b) ~ t(Internal a -> Internal a) ~ t被添加到Wanted组,但是这将是一个相当长的读。


0

您是否在GHC 8.8+中尝试过?

share :: (Let :<: sup,
          Domain a ~ sup,
          Domain b ~ sup,
          SyntacticN (a -> (a -> b) -> b) fi,
          _) 
      => a -> (a -> b) -> a
share = sugarSym Let

关键是在约束中使用类型孔: _ => your difficult type

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.