追踪约束的技巧

本文翻译自:Techniques for Tracing Constraints

Here's the scenario: I've written some code with a type signature and GHC complains could not deduce x ~ y for some x and y . 这是一个场景:我写了一些带有类型签名的代码,GHC抱怨无法推断xy某些xy You can usually throw GHC a bone and simply add the isomorphism to the function constraints, but this is a bad idea for several reasons: 你通常可以将GHC作为一个骨骼并简单地将同构函数添加到函数约束中,但这有几个原因:

  1. It does not emphasize understanding the code. 它并不强调理解代码。
  2. You can end up with 5 constraints where one would have sufficed (for example, if the 5 are implied by one more specific constraint) 你可以得到5个约束,其中一个就足够了(例如,如果5个被一个更具体的约束暗示)
  3. You can end up with bogus constraints if you've done something wrong or if GHC is being unhelpful 如果你做错了什么或者GHC没有帮助,你最终可能会受到假的限制

I just spent several hours battling case 3. I'm playing with syntactic-2.0 , and I was trying to define a domain-independent version of share , similar to the version defined in NanoFeldspar.hs . 我只花了几个小时与案例3作斗争。我正在使用syntactic-2.0 ,我试图定义一个与域无关的share版本,类似于NanoFeldspar.hs定义的版本。

I had this: 我有这个:

{-# 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

and GHC could not deduce (Internal a) ~ (Internal b) , which is certainly not what I was going for. 和GHC could not deduce (Internal a) ~ (Internal b) ,这当然不是我想要的。 So either I had written some code I didn't intend to (which required the constraint), or GHC wanted that constraint due to some other constraints I had written. 所以要么我写了一些我不想要的代码(需要约束),要么GHC想要这个约束,因为我写了一些其他的约束。

It turns out I needed to add (Syntactic a, Syntactic b, Syntactic (a->b)) to the constraint list, none of which imply (Internal a) ~ (Internal b) . 事实证明我需要将(Syntactic a, Syntactic b, Syntactic (a->b))到约束列表中,其中没有一个暗示(Internal a) ~ (Internal b) I basically stumbled upon the correct constraints; 我基本上偶然发现了正确的限制; I still don't have a systematic way to find them. 我仍然没有系统的方法来找到它们。

My questions are: 我的问题是:

  1. Why did GHC propose that constraint? 为什么GHC提出这个约束? Nowhere in syntactic is there a constraint Internal a ~ Internal b , so where did GHC pull that from? 语法中没有任何内容有一个约束Internal a ~ Internal b ,那么GHC从哪里拉出来?
  2. In general, what techniques can be used to trace the origin of a constraint which GHC believes it needs? 一般而言,可以使用哪些技术来追踪GHC认为需要的约束的起源? Even for constraints that I can discover myself, my approach is essentially brute forcing the offending path by physically writing down recursive constraints. 即使对于我可以发现自己的约束,我的方法本质上是通过物理写下递归约束来强制违规路径。 This approach is basically going down an infinite rabbit hole of constraints and is about the least efficient method I can imagine. 这种方法基本上是一个无限的约束兔子洞,是我能想象的效率最低的方法。

#1楼

参考:https://stackoom.com/question/1aNvy/追踪约束的技巧


#2楼

First of all, your function has the wrong type; 首先,你的函数有错误的类型; I am pretty sure it should be (without the context) a -> (a -> b) -> b . 我很确定它应该是(没有上下文) a -> (a -> b) -> b GHC 7.10 is somewhat more helpful in pointing that out, because with your original code, it complains about a missing constraint Internal (a -> b) ~ (Internal a -> Internal a) . GHC 7.10在指出这一点时更有帮助,因为使用原始代码,它会抱怨缺少约束Internal (a -> b) ~ (Internal a -> Internal a) After fixing share 's type, GHC 7.10 remains helpful in guiding us: 在确定share类型后,GHC 7.10仍然有助于指导我们:

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

  2. After adding the above, we get Could not deduce (sup ~ Domain (a -> b)) 添加上述内容后,我们得到Could not deduce (sup ~ Domain (a -> b))

  3. After adding that, we get Could not deduce (Syntactic a) , Could not deduce (Syntactic b) and Could not deduce (Syntactic (a -> b)) 添加之后,我们得到Could not deduce (Syntactic a)Could not deduce (Syntactic b)Could not deduce (Syntactic (a -> b))

  4. After adding these three, it finally typechecks; 加上这三个后,它终于出现了问题; so we end up with 所以我们最终得到了

     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 

So I'd say GHC hasn't been useless in leading us. 所以我说GHC在领导我们方面毫无用处。

As for your question about tracing where GHC gets its constraint requirements from, you could try GHC's debugging flags , in particular, -ddump-tc-trace , and then read through the resulting log to see where Internal (a -> b) ~ t and (Internal a -> Internal a) ~ t are added to the Wanted set, but that will be quite a long read. 至于关于追踪GHC获取其约束要求的问题,您可以尝试GHC的调试标志 ,特别是-ddump-tc-trace ,然后读取结果日志以查看Internal (a -> b) ~ t -ddump-tc-trace(Internal a -> Internal a) ~ t被添加到Wanted集,但这将是相当长的阅读。

你可能感兴趣的:(haskell,constraints,ghc)