mono-traversableの「concatMap」はどのようにして一般的な議論を「引き出す」ことができますか?


9

私はHaskellを学んでいて、理解しにくいこの動作に出くわしたときに、Yesodのために単純なDBシードプログラムを実行していました。

testFn :: Int -> Bool -> [Int]
testFn a b = if b then replicate 10 a else []

Yesod GHCIセッション:

$ :t concatMap testFn [3]
concatMap testFn [3] :: Bool -> [Int]
$ (concatMap testFn [1,2,3]) True
[1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3]

どういうわけか、各マッピングからその2番目の「ブール値」を単一のカレー引数に「引き出す」ことができました。

標準ベースのプレリュードGHCIセッションでは、この式のコンパイルも拒否されます。

$ :t concatMap testFn [3]
error:
     Couldn't match type 'Bool -> [Int]' with '[b]'
      Expected type: Int -> [b]
        Actual type: Int -> Bool -> [Int]
     Probable cause: 'testFn' is applied to too few arguments
      In the first argument of 'concatMap', namely 'testFn'
      In the expression: concatMap testFn [3]

Yesod は、独自の独自の横断可能なライブラリを使用していることがわかりましたconcatMap

$ :t concatMap
concatMap
  :: (MonoFoldable mono, Monoid m) =>
     (Element mono -> m) -> mono -> m

現在のHaskell理解のレベルでは、ここで型がどのように分散されているのか理解できませんでした。誰かが私に(できるだけ多くの初心者向け)このトリックがどのように行われるか説明できますか?testFn上記のどの部分がElement monoタイプに準拠していますか?

回答:


6

まず、既知のタイプをいくつかリストします。(数字はInt単純化するためのものであると仮定します-これは実際には関係ありません。)

testFn :: Int -> Bool -> [Int]
[1,2,3] :: [Int]
True :: Bool

(concatMap testFn [1,2,3]) Trueはと同じconcatMap testFn [1,2,3] Trueであるため、concatMapこれらのすべての引数に一致する型が必要です。

concatMap :: (Int -> Bool -> [Int]) -> [Int] -> Bool -> ???

どこ???結果タイプがあります。関連付けルールにより、->は右側に関連付けられるため、上記の入力は次と同じであることに注意してください。

concatMap :: (Int -> (Bool -> [Int])) -> [Int] -> (Bool -> ???)

その上に一般的なタイプを書いてみましょう。類似性を示すためにいくつかのスペースを追加しています。

concatMap :: (MonoFoldable mono, Monoid m) =>
             (Element mono -> m              ) -> mono  -> m
concatMap :: (Int          -> (Bool -> [Int])) -> [Int] -> (Bool -> ???)

あはは!mas Bool -> [Int]monoas を選択すると一致します[Int]。その場合、制約を満たしMonoFoldable mono, Monoid m(下記参照)、さらにもあるElement mono ~ Intので、すべての型チェックが行われます。

これ???[Int]の定義から推測できmます。

制約について:については、MonoFoldable [Int]言うことはほとんどありません。[Int]明らかにInt要素型を持つリストのような型であり、それをMonaFoldablewith Intas にするのに十分Elementです。

以下のためにMonoid (Bool -> [Int])、それは少し複雑。A -> Bモノイドの場合、関数型はモノイドであることBがわかります。これは、点ごとに操作を実行することによって続きます。私たちの特定のケースでは[Int]、モノイドであることに依存しており、次のようになります。

mempty :: Bool -> [Int]
mempty = \_ -> []

(<>) :: (Bool -> [Int]) -> (Bool -> [Int]) -> (Bool -> [Int])
f <> g = \b -> f b ++ g b

1
素晴らしい説明!あなたが説明して型を揃えた方法は、私が見たかったものです、ありがとう!
dimsuz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.