web-dev-qa-db-ja.com

制約をトレースするためのテクニック

シナリオは次のとおりです。タイプシグネチャを使用していくつかのコードを記述しましたが、GHCは一部のxおよびyについてx〜yを推測できませんでした。通常、GHCにボーンをスローして、関数の制約に同型を追加することができますが、これはいくつかの理由で悪い考えです。

  1. コードを理解することは重要ではありません。
  2. 最終的には、5つの制約で十分な場合があります(たとえば、5がもう1つの特定の制約によって暗示されている場合)
  3. 何か間違ったことをした場合や、GHCが役に立たない場合は、偽の制約になる可能性があります

ケース3との戦いに数時間費やしました。 syntactic-2.0 で遊んでいます。そして、 NanoFeldspar.hs で定義されたバージョンと同様に、ドメインに依存しないバージョンのshareを定義しようとしました。 =。

私はこれを持っていました:

{-# 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が必要と考える制約の起源を追跡するためにどのような手法を使用できますか?私がcan自分自身を発見した制約であっても、私のアプローチは本質的に、再帰的な制約を物理的に書き留めることで、問題のあるパスを強引に強制します。このアプローチは基本的に、ウサギの制約の無限の穴を下っており、私が想像できる最も効率の悪い方法です。
321
crockeea

まず、関数の型が間違っています。 (コンテキストなしで)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. これら3つを追加すると、最終的に型チェックが行われます。だから私たちは

    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
    

ですから、GHCは私たちを導くのに役に立たなかったとは思いません。

GHCが制約要件を取得する場所のトレースに関する質問については、 GHCのデバッグフラグ 、特に-ddump-tc-traceを試してから、結果のログを読んでInternal (a -> b) ~ t(Internal a -> Internal a) ~ tが追加されている場所を確認できますWantedを設定しますが、それは非常に長い読み取りになります。

5
Cactus