Haskell 跟踪约束的技术

Haskell 跟踪约束的技术,haskell,constraints,ghc,Haskell,Constraints,Ghc,下面是一个场景:我编写了一些带有类型签名的代码,GHC投诉无法推断某些x和y的x~y。通常,您可以抛出GHC骨骼,然后简单地将同构添加到函数约束中,但这是一个坏主意,原因如下: 它并不强调理解代码 您可以在一个约束已经足够的情况下得到5个约束(例如,如果5个约束由一个更具体的约束暗示) 如果你做错了什么,或者GHC无济于事,你最终可能会受到虚假的约束 我刚刚花了几个小时和第三个案子作斗争。我正在玩,我试图定义一个独立于域的共享版本,类似于中定义的版本 我有这个: {-# LANGUAGE GAD

下面是一个场景:我编写了一些带有类型签名的代码,GHC投诉无法推断某些
x
y
的x~y。通常,您可以抛出GHC骨骼,然后简单地将同构添加到函数约束中,但这是一个坏主意,原因如下:

  • 它并不强调理解代码
  • 您可以在一个约束已经足够的情况下得到5个约束(例如,如果5个约束由一个更具体的约束暗示)
  • 如果你做错了什么,或者GHC无济于事,你最终可能会受到虚假的约束
  • 我刚刚花了几个小时和第三个案子作斗争。我正在玩,我试图定义一个独立于域的
    共享版本
    ,类似于中定义的版本

    我有这个:

    {-# 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
    
    {-#语言GADT、灵活上下文、类型运算符#-}
    导入数据。语法
    --基于纳米长石
    数据泄露到哪里
    Let::Let(a:->(a->b):->完全b)
    共享:(让:(a->b)->b)fi)
    =>a->(a->b)->a
    分享
    
    而GHC
    无法推断(内部a)~(内部b)
    ,这肯定不是我想要的。所以要么我写了一些我不想写的代码(这需要约束),要么GHC因为我写的一些其他约束而想要那个约束

    事实证明我需要将
    (语法a,语法b,语法(a->b))
    添加到约束列表中,所有这些都不意味着
    (内部a)~(内部b)
    。我基本上偶然发现了正确的约束条件;我仍然没有一个系统的方法来找到它们

    我的问题是:

  • GHC为什么提出这一限制?句法上没有约束
    内部a~b
    ,那么GHC从哪里得到的呢
  • 一般来说,可以使用什么技术来追踪GHC认为需要的约束的来源?即使对于我自己可以发现的约束,我的方法本质上是通过物理地写下递归约束来强制执行有问题的路径。这种方法基本上是在无限的约束下进行的,是我能想象到的效率最低的方法

  • 首先,你的函数有错误的类型;我很确定它应该是(没有上下文)
    a->(a->b)->b
    。GHC7.10在指出这一点上更有帮助,因为对于原始代码,它抱怨缺少约束
    内部(a->b)~(内部a->内部a)
    。在确定了共享的类型之后,GHC 7.10仍然有助于指导我们:

  • 无法推断(内部(a->b)~(内部a->内部b))

  • 添加以上内容后,我们得到
    无法推断(sup~域(a->b))

  • 添加后,我们得到
    无法推断(语法a)
    无法推断(语法b)
    无法推断(语法a->b))

  • 在加上这三个之后,它最终进行了类型检查;因此,我们以

    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
    
    share::(Let:b)~sup,
    内部(a->b)~(内部a->内部b),
    句法a,句法b,句法(a->b),
    语法(a->(a->b)->b)fi)
    =>a->(a->b)->b
    分享
    
  • 所以我要说GHC在领导我们方面并没有什么用处


    至于您关于跟踪GHC从何处获得其约束要求的问题,您可以尝试,特别是,
    -ddump tc trace
    ,然后阅读生成的日志,查看将
    内部(a->b)~t
    (内部a->内部a)~t
    添加到
    通缉
    集中的位置,但这将是一个相当长的阅读过程。

    您在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
    
    share::(让:(a->b)->b)fi,
    _) 
    =>a->(a->b)->a
    分享
    

    关键是在约束中使用类型洞:
    \u=>您的困难类型

    关于类型级调试器有一些讨论,但普遍的共识似乎表明,typechecker的内部逻辑不会有任何帮助:/Haskell的约束求解器是一种蹩脚的不透明逻辑语言:)@jozefg你有讨论的链接吗?通常只需完全删除类型签名,让ghci告诉你它对签名的看法就可以了应该是。不知何故,
    a
    b
    是绑定的-在上下文之外查看类型签名-
    a->(a->b)->a
    ,而不是
    a->(a->b)->b
    。也许就是这样?使用约束解算器,它们可以在任何地方影响传递等式,但错误通常显示出一个位置“靠近”约束被诱导的位置。尽管@jozefg-可能会用标签或其他东西来注释约束,以显示它们来自何处,但这还是很酷的:s