Как определить, почему ghc ищет конкретный экземпляр класса?
Когда экземпляры класса условного типа работают вглубь, может быть трудно понять, почему ghc жалуется на экземпляр класса отсутствующего типа. Например:
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
| ^^^
Предположим, что я только написал определения для foo
и bar
, а все остальное было импортированным кодом, который я не писал, я могу быть очень смущен, почему ghc пытается найти экземпляр MyClass1
для Int
. Это может быть даже в первый раз, когда я когда-либо слышал о классе MyClass1
, если до сих пор я полагался на импортированные экземпляры. Было бы неплохо, если бы ghc мог дать мне "трассировку стека" в цепочке экземпляров класса типа, например
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 дает мне свою ошибку. Какие инструменты можно использовать для отслеживания проблемы?
Ответы
Ответ 1
Вы можете попробовать параметр -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
Нелегко извлечь полезную информацию из этого дампа, но AFAIK это единственный доступный вариант прямо сейчас. Немного связанных билетов: 13443, 15044
ADDED: Я попытаюсь немного объяснить, что означает свалка. Я действительно не знаком с внутренними структурами 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
обозначает "глубина", WD
- "требуемое производное" ограничение. Таким образом, у нас есть что - то вроде трассировки стека разыскиваемых ограничений: сначала мы хотели MyClass3 (MyType2 (MyType1 Int))
, то мы нашли MyClass3
экземпляр для MyType2
, и теперь мы хотим MyClass2 (MyType1 Int)
, чтобы удовлетворить его. Затем мы нашли экземпляр MyClass2
для MyType1
, теперь мы хотим, чтобы MyClass1 Int
удовлетворял его. Я знаю, объяснение расплывчато, но это все, что у меня есть для вас, извините.