我如何确定为什么 ghc 正在寻找特定类型的 class 实例?

How do I determine why ghc is looking for a particular type class instance?

当条件类型 class 实例 运行 很深时,可能很难弄清楚为什么 ghc 会抱怨缺少类型 class 实例。例如:

class MyClass1 c
class MyClass2 c
class MyClass3 c

data MyType1 a
data MyType2 a

instance MyClass1 a => MyClass2 (MyType1 a)
instance MyClass2 a => MyClass3 (MyType2 a)

foo :: (MyClass3 c) => c
foo = undefined

bar :: MyType2 (MyType1 Int)
bar = foo

GHC 给出以下错误:

Example.hs:149:7-9: error:
    • No instance for (MyClass1 Int) arising from a use of ‘foo’
    • In the expression: foo
      In an equation for ‘bar’: bar = foo
    |
149 | bar = foo
    |       ^^^

假设我只写了 foobar 的定义,而其他一切都是我没有写的导入代码,我可能会很困惑为什么 ghc 试图找到一个MyClass1 Int 的实例。如果到目前为止我一直依赖于导入的实例,这甚至可能是我第一次听说 MyClass1 class。如果 ghc 能给我一个 "stack trace" 类型的 class 实例链就好了,例如

Sought (MyClass2 (MyType1 Int)) to satisfy (MyClass3 (MyType2 (MyType1 Int))) from conditional type class instance OtherModule.hs:37:1-18
Sought (MyClass1 Int) to satisfy (MyClass2 (MyType1 Int)) from conditional type class instance OtherModule.hs:36:1-18

ghc 有命令行选项吗?如果没有,我该如何调试?

请记住,我的实际问题比这个简单的例子复杂得多。例如

Search.hs:110:31-36: error:
    • Could not deduce (Ord
                          (Vars (DedupingMap (Rep (Index gc)) (IndexedProblem ac))))
        arising from a use of ‘search’
      from the context: (PP gc (IndexedProblem ac),
                         Show (Vars (DedupingMap (Rep (Index gc)) (IndexedProblem ac))),
                         Foldable f, MonadNotify m)
        bound by the type signature for:
                   searchIndexedReplicaProblem :: forall gc ac (f :: * -> *) (m :: *
                                                                                   -> *).
                                                  (PP gc (IndexedProblem ac),
                                                   Show
                                                     (Vars
                                                        (DedupingMap
                                                           (Rep (Index gc)) (IndexedProblem ac))),
                                                   Foldable f, MonadNotify m) =>
                                                  f (Index
                                                       (Clzs
                                                          (PartitionedProblem
                                                             gc (IndexedProblem ac))))
                                                  -> m (Maybe
                                                          (Vars
                                                             (PartitionedProblem
                                                                gc (IndexedProblem ac))))
        at Search.hs:(103,1)-(109,131)
    • In the expression: search
      In an equation for ‘searchIndexedReplicaProblem’:
          searchIndexedReplicaProblem = search
    |
110 | searchIndexedReplicaProblem = search
    |                               ^^^^^^

PP 有五个覆盖条件,我使用的是类型族和不可判定的实例,所以 ghc 给我它的错误的原因非常不明显。我可以使用哪些工具来追踪问题?

您可以尝试 -ddump-cs-trace 选项,虽然它旨在帮助 GHC 开发人员调试约束解决代码,但它也可能对您有用。这是您的示例的输出:

Step 1[l:2,d:0] Kept as inert:
    [G] $dMyClass3_a1rt {0}:: MyClass3 c_a1rs[sk:2]
Step 2[l:2,d:0] Dict equal (keep):
    [WD] $dMyClass3_a1rv {0}:: MyClass3 c_a1rs[sk:2]
Constraint solver steps = 2
Step 1[l:1,d:0] Top react: Dict/Top (solved wanted):
    [WD] $dMyClass3_a2uc {0}:: MyClass3 (MyType2 (MyType1 Int))
Step 2[l:1,d:1] Top react: Dict/Top (solved wanted):
    [WD] $dMyClass2_a2up {1}:: MyClass2 (MyType1 Int)
Step 3[l:1,d:2] Kept as inert:
    [WD] $dMyClass1_a2uq {2}:: MyClass1 Int
Step 4[l:2,d:0] Kept as inert:
    [G] $dMyClass3_a1rB {0}:: MyClass3 c_a1rz[sk:2]
Step 5[l:2,d:0] Wanted CallStack IP:
    [WD] $dIP_a2u8 {0}:: ?callStack::GHC.Stack.Types.CallStack
Step 6[l:2,d:0] Kept as inert:
    [WD] $dIP_a2uA {0}:: ?callStack::GHC.Stack.Types.CallStack
Step 7[l:2,d:0] Kept as inert:
    [G] $dMyClass2_a2uh {0}:: MyClass2 a_a2ug[ssk:2]
Step 8[l:2,d:0] Kept as inert:
    [G] $dMyClass1_a2ul {0}:: MyClass1 a_a2uk[ssk:2]
Constraint solver steps = 8

从这个转储中提取有用的信息并不容易,但据我所知,这是目前唯一可用的选项。几张相关工单:13443, 15044

添加: 我将尝试解释一下转储的含义。我实际上并不熟悉 GHC 内部结构,所以这只是我的(可能是错误的)理解。

相关位是下一个:

Step 1[l:1,d:0] Top react: Dict/Top (solved wanted):
    [WD] $dMyClass3_a2uc {0}:: MyClass3 (MyType2 (MyType1 Int))
Step 2[l:1,d:1] Top react: Dict/Top (solved wanted):
    [WD] $dMyClass2_a2up {1}:: MyClass2 (MyType1 Int)
Step 3[l:1,d:2] Kept as inert:
    [WD] $dMyClass1_a2uq {2}:: MyClass1 Int

这里的d代表"depth",WD是"wanted derived"约束。所以我们有一些类似所需约束的堆栈跟踪:最初我们想要 MyClass3 (MyType2 (MyType1 Int)),然后我们找到 MyType2MyClass3 实例,现在我们想要 MyClass2 (MyType1 Int) 来满足它.然后我们找到了MyType1MyClass2实例,现在我们要MyClass1 Int来满足它。我知道,解释含糊不清,但我只能告诉你这些,对不起。