関数の理由を理解しようとしています
map (filter fst)
タイプを持っています
[[(Bool, a)]] -> [[(Bool, a)]]
フィルターがブール型を返す関数を受け取る必要があり、fst がタプルの最初の要素を返すだけの場合、「フィルター fst」はどのように機能しますか?
filter :: (a -> Bool) -> [a] -> [a]
fst :: (a, b) -> a
誰でも私を説明できますか?ありがとう ;)
関数の理由を理解しようとしています
map (filter fst)
タイプを持っています
[[(Bool, a)]] -> [[(Bool, a)]]
フィルターがブール型を返す関数を受け取る必要があり、fst がタプルの最初の要素を返すだけの場合、「フィルター fst」はどのように機能しますか?
filter :: (a -> Bool) -> [a] -> [a]
fst :: (a, b) -> a
誰でも私を説明できますか?ありがとう ;)
フィルターがブール型を返す関数を受け取る必要があり、fst がタプルの最初の要素を返すだけの場合、「フィルター fst」はどのように機能しますか?
ある意味で、あなたはあなた自身の質問に答えました! それを分解しましょう:
フィルタは Bool-Type を返す関数を受け取る必要があります
では、何を渡すか見てみましょう: fst
. fst
関数ですか?はい、そうです。最初の部分を下に置きました。を返しBool
ますか?さて、それが何をするか見てみましょう:
fst はタプルの最初の要素を返すだけです
したがって、タプルの最初の要素が a の場合Bool
、はい、bool を返します! ただし、タプルの最初の要素が a 以外のBool
場合、型チェックは失敗し、失敗します。
あなたが立てたタイプをもう一度見てみましょう。わかりやすくするために、型変数の名前を変更します。
filter :: (a -> Bool) -> [a] -> [a]
fst :: (b, c) -> b
fst
を取り、(b, c)
を返します。フィルタは、 を取り、を返すb
関数を期待しています。を渡しているので、上記はの最初のパラメーターである必要があります。渡す関数の戻り値はa でなければならないため、上記は aでなければなりません。フィルターではまったく使用されないため、何でもかまいません。との値を代入すると、 ofの最終的な型が得られます。a
Bool
fst
a
(b, c)
fst
filter
Bool
b
Bool
c
a
b
filter fst
filter fst :: [(Bool, c)] -> [(Bool, c)]
最後に、のタイプmap
は次のとおりです。
map :: (d -> e) -> [d] -> [e]
(繰り返しますが、ここで型変数の名前を変更しました。これは、上記で使用したものと区別するためですが、型変数のスコープ内で一貫している限り、それらが何と呼ばれているかは実際には問題ではないことを覚えておいてください。型注釈)
map (filter fst)
filter fst
上で定義した を最初のパラメータとして に渡しますmap
。のパラメータd
と の結果を置き換えると、e
この関数は でなければならない[(Bool, c)] -> [(Bool, c)]
、つまりd
との両方e
がであることがわかります(Bool, c)
。それらを関数にプラグインすると、最終的な型に到達します。
map (filter fst) :: [[(Bool, c)]] -> [[(Bool, c)]]
fst
タプルを最初の要素としてブール値を持つように制限する限り、ブール値を返す関数です (2 番目の要素は何でもかまいません。したがって、(Bool, a)
:t filter -- (a -> Bool) -> [a] -> [a]
:t fst -- (a,b) -> a
ただし、型変数を交換しfst
て取得することもできます。
:t fst -- (c,b) -> c
したがって、 の最初の引数filter
は typeですがa -> Bool
、fst
それ自体は(c,b) -> c
です。今、これを結合しようとしています (これは統合と呼ばれていると思います):
1st arg of filter: a -> Bool
fst: (c,b) -> c
このことから、(右辺が等しくc
なければならないため) である必要があると推測し、次を取得できます。Bool
1st arg of filter: a -> Bool
fst: (Bool,b) -> Bool
a
上記から、 である必要があると推測し(Bool,b)
、以下を取得します。
1st arg of filter: (Bool,b) -> Bool
fst: (Bool,b) -> Bool
これで完了です。
他の人が行ったように、ここで型方程式を解きたいと思います。しかし、より視覚的な方法でそれらを書き留めたいので、導出はより自動化された方法で実行できます。どれどれ。
filter :: ( a -> Bool) -> [a] -> [a]
fst :: (c,d) -> c -- always use unique type var names,
------------------------ -- rename freely but consistently
filter fst :: [a] -> [a], a -> Bool ~ (c,d) -> c
a ~ (c,d) , Bool ~ c
a ~ (Bool,d)
---------------------------
:: [(Bool,d)] -> [(Bool,d)]
純粋に機械的なもの。:) これとともに、
map :: ( a -> b ) -> [a] -> [b]
filter fst :: [(Bool,d)] -> [(Bool,d)]
------------------------------
map (filter fst) :: [a] -> [b], a ~ [(Bool,d)] , b ~ [(Bool,d)]
-------------------------------
:: [[(Bool,d)]] -> [[(Bool,d)]]
最終的な型の型変数は、読みやすくするために (もちろん一貫した方法で) 自由に名前を変更できます。
ここでの他の回答ですでに示されているものに私の回答が追加する唯一のことは、このアドバイスです(これは重要だと思います):あるものを他のものの下に書き留めるというこの単純な規律に従うと、これらのタイプを実行するのが非常に簡単になります非常に機械的で自動的な方法での統合 (したがって、エラーの可能性を減らします)。
実際の型派生 Prolog プログラムを含むこの別の例については、Haskell: how to infer the type of an expression manual を参照してください。
ダニエルプライトの回答が投稿される前にこれを書き留めたので、とにかく投稿します。ここのタイプの思考プロセスをたどっていfilter fst
ます。
まず、型シグネチャを書き留めます (その型変数名がフィルターのものと衝突しないように fst を変更します):
filter :: (a -> Bool) -> [a] -> [a]
fst :: (b, c) -> b
(a -> Bool)
と一致((b, c) -> b)
:
b
でなければならないBool
、つまり、そうでa
なければならない(Bool,c)
この情報に特化するfilter
と、次のようになります。
filter :: ((Bool,c) -> Bool) -> [(Bool,c)] -> [(Bool,c)]
につながる
filter fst :: [(Bool, c)] -> [(Bool, c)]