Haskell Language
型付きの穴
サーチ…
備考
Haskellの強みの1つは、タイプシステムで問題のドメインの一部をモデル化するためにタイプシステムを活用する能力です。そうすることで、非常に複雑なタイプに遭遇することがよくあります。これらの型(つまり、これらの型を持つ値)を持つプログラムを書くとき、時にはすべての型を「ジャグリング」することがほぼ不可能になります。 GHC 7.8以降、型付き穴という新しい構文上の特徴があります。型付きの穴は、コア言語のセマンティクスを変更しません。彼らはプログラムを書くための援助として純粋に意図されています。
型付き穴の詳細な説明と、型付き穴の設計については、 Haskell wikiを参照してください。
タイプされた穴の構文
型付きの穴は、単一のアンダースコア( _
)または有効なHaskell識別子であり、式コンテキストではスコープ内にありません。型付きの穴が存在する前に、これらの両方がエラーを引き起こすので、新しい構文は古い構文を妨げません。
型付き穴の動作を制御する
型付き穴のデフォルトの動作は、型付き穴に遭遇したときにコンパイル時エラーを生成することです。ただし、動作を微調整するためのフラグがいくつかあります。これらのフラグは次のように要約されています( GHC trac )。
デフォルトでは、GHCは型付き穴を使用可能にし、型付き穴に遭遇するとコンパイル・エラーを生成します。
-fdefer-type-errors
または-fdefer-typed-holes
を有効にすると、穴のエラーは警告に変換され、評価されるとランタイムエラーが発生します。
-fwarn-typed-holes
という警告フラグは、デフォルトでオンになっています。-fdefer-type-errors
または-fdefer-typed-holes
なければ、これらの条件では型付きの穴がエラーになるため、このフラグはno-opです。いずれかの遅延フラグが有効な場合(型付きのホール・エラーを警告に変換する)、--fno-warn-typed-holes
フラグは警告を無効にします。つまり、コンパイルが正常に終了し、穴を評価するとランタイムエラーが発生します。
型付き穴のセマンティクス
型の穴の値は、 undefined
れていundefined
と簡単に言うことができますが、型付きの穴はコンパイル時のエラーを引き起こすため、厳密に値を割り当てる必要はありません。しかし、型付きの穴(有効にすると)は、型付き穴の名前、推論された最も一般的な型、およびすべてのローカルバインディングの種類を示すコンパイル時エラー(または遅延型エラーの警告)を生成します。例えば:
Prelude> \x -> _var + length (drop 1 x)
<interactive>:19:7: Warning:
Found hole `_var' with type: Int
Relevant bindings include
x :: [a] (bound at <interactive>:19:2)
it :: [a] -> Int (bound at <interactive>:19:1)
In the first argument of `(+)', namely `_var'
In the expression: _var + length (drop 1 x)
In the expression: \ x -> _var + length (drop 1 x)
上記のようにGHCi replに入力された式に型付きの穴がある場合、入力された式のタイプも報告されit
(ここでは[a] -> Int
)。
タイプされた穴を使用してクラスインスタンスを定義する
型付きの穴は、対話型のプロセスを通じて、関数を簡単に定義することができます。
Foo
インスタンスが必要な多相ライブラリ関数で使用するために、カスタムのBar
型のFoo Bar
クラスインスタンスを定義したいとします。伝統的に、 Foo
のドキュメントを調べ、定義する必要のあるメソッドを特定し、型を調べます。型付きの穴を使用すると、実際にはスキップすることができます。
最初にダミーインスタンスを定義します。
instance Foo Bar where
コンパイラは今すぐ文句を言うでしょう
Bar.hs:13:10: Warning:
No explicit implementation for
‘foom’ and ‘quun’
In the instance declaration for ‘Foo Bar’
foom
、 Bar
ためにfoom
を定義する必要があります。しかしそれは何であるべきであろう?やはりドキュメントを見るのが面倒すぎて、コンパイラに尋ねるだけです。
instance Foo Bar where
foom = _
ここでは、単純な「ドキュメント照会」として型付きの穴を使用しました。コンパイラの出力
Bar.hs:14:10:
Found hole ‘_’ with type: Bar -> Gronk Bar
Relevant bindings include
foom :: Bar -> Gronk Bar (bound at Foo.hs:4:28)
In the expression: _
In an equation for ‘foom’: foom = _
In the instance declaration for ‘Foo Bar’
コンパイラがすでにクラス型変数に、具体化する型のBar
を埋め込み、それをインスタンス化する方法に注目してください。これにより、クラスのドキュメントにある多型よりもシグネチャを理解しやすくなります。特に、マルチパラメータ型のクラスなどのより複雑なメソッドを扱う場合は、シグネチャをもっと理解しやすくなります。
しかし、 Gronk
はGronk
何ですか?この時点で、 Hayooに聞くことはおそらく良い考えです。しかし、私たちはそれがなくてもまだ逃げるかもしれません。盲目的な推測として、これは型コンストラクタだけでなく、単一値コンストラクタでもあるとGronk a
ます。 Gronk a
何らかの形でGronk a
値を生成する関数として使用できます。私たちは試してみる
instance Foo Bar where
foom bar = _ Gronk
運がGronk
ば、 Gronk
は実際には価値があり、コンパイラは
Found hole ‘_’
with type: (Int -> [(Int, b0)] -> Gronk b0) -> Gronk Bar
Where: ‘b0’ is an ambiguous type variable
Gronk
、 Gronk
は2つの議論があることに注意してください。私たちはその試みを洗練することができます:
instance Foo Bar where
foom bar = Gronk _ _
これは今かなり明確です:
Found hole ‘_’ with type: [(Int, Bar)]
Relevant bindings include
bar :: Bar (bound at Bar.hs:14:29)
foom :: Bar -> Gronk Bar (bound at Foo.hs:15:24)
In the second argument of ‘Gronk’, namely ‘_’
In the expression: Gronk _ _
In an equation for ‘foom’: foom bar = Gronk _ _
たとえば、 bar
値を分解することによってさらに進歩することができます( Relevant bindings
セクションでは、コンポーネントがタイプとともに表示されRelevant bindings
)。多くの場合、正しい定義が何であるかはっきりとはっきりしています。なぜなら、使用可能なすべての引数とタイプがジグソーパズルのようにまとまっているからです。あるいは、定義が不可能であり、その理由がわかるかもしれません。
これらのすべてはインタラクティブなコンパイルのエディタ、例えばhaskell-modeを使ったEmacsでうまく動作します。 IDEのマウスオーバー値クエリと同じように、入力された穴を、解釈された動的命令言語のために使用できますが、すべての制限はありません。