追踪约束的技巧

时间:2014-05-03 18:11:43

标签: haskell constraints ghc

以下是场景:我编写了一些带有类型签名的代码,而GHC抱怨无法推断某些xy的x~y。你通常可以将GHC作为一个骨骼并简单地将同构函数添加到函数约束中,但出于以下几个原因这是一个坏主意:

  1. 它并不强调理解代码。
  2. 你最终可以得到5个约束,其中一个就足够了(例如,如果5个被一个更具体的约束暗示)
  3. 如果你做错了或者GHC没有帮助,你最终可能会受到假的限制
  4. 我只花了几个小时与案例3作斗争。我正在玩syntactic-2.0,我正在尝试定义与share无关的域版本,类似于{{3}中定义的版本}}

    我有这个:

    {-# 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认为需要的约束的起源?即使对于我可以发现自己的约束,我的方法本质上是通过物理写下递归约束来强制违规路径。这种方法基本上是一个无限的限制兔子洞,是我能想象的效率最低的方法。

2 个答案:

答案 0 :(得分:5)

首先,你的函数类型错误;我很确定它应该是(没有上下文)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
    
  5. 所以我说GHC在领导我们方面毫无用处。

    关于跟踪GHC获取约束要求的问题,您可以尝试GHC's debugging flags,尤其是-ddump-tc-trace,然后阅读生成的日志以查看Internal (a -> b) ~ t的位置并且(Internal a -> Internal a) ~ t被添加到Wanted集,但这将是一个很长的阅读。

答案 1 :(得分: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