2

Haskellの表現がどのように機能するかを理解していないと、それをより基本的な形に分解するのに役立つことがよくあります。

次の定義を使用する

sequenceA :: (Applicative f) => [f a] -> f [a]
sequenceA [] = pure []
sequenceA (x:xs) = (:) <$> x <*> sequenceA xs

instance Applicative ((->) r) where
    pure x = (\_ -> x)
    f <*> g = \x -> f x (g x)

私は次のように書き直しsequenceA [(+3),(+2)] 3ました

(\_ -> (:)) <*> (+3) <*> ((\_ -> (:)) <*> (+2) <*> (\_-> [])) $ 3

そしてそれをに変えました(フォーマットを許してください;私は行を分割するための規則が何であるかわかりません)

(\d ->(\c->(\b -> (\a -> (\_ -> (:)) a (+3) a) b (\_ -> (:)) b) c (+2) c) d (\_ -> []) d) 3

手作業で作業する場合は正しいように見えますが、GHCiに受け入れてもらうことはできません。私はここで何を間違えましたか?私の2番目の質問は、この形式から機能合成に変換する方法です。ドットをさまざまな組み合わせで置き換えてみましたが、GHCiはそれらすべてを拒否します。

4

3 に答える 3

7

怠け者なので、コンピュータに拡張を任せようと思った。だからGHCiに、私はタイプした

let pu x = "(\\_ -> " ++ x ++ ")"
let f >*< a = "(\\g -> " ++ f ++ " g (" ++ a ++ " g))"

これで、式のように見える文字列をより複雑な式のように見える文字列にマップする面白いバージョンのpureandができました。<*>次に、同様に の類似物を定義し、sequenceA関数を文字列に置き換えました。

let sqa [] = pu "[]" ; sqa (f : fs) = (pu "(:)" >*< f) >*< sqa fs

次に、次のように例の展開された形式を生成できました

putStrLn $ sqa ["(+3)","(+2)"] ++ " 3"

正式に印刷されたもの

(\g -> (\g -> (\_ -> (:)) g ((+3) g)) g ((\g -> (\g -> (\_ -> (:)) g ((+2) g)) g  ((\_ -> []) g)) g)) 3

この最後の、プロンプトにコピーされた、生成された

[6,5]

私の「メタプログラム」からの出力を問題の試行と比較すると、<*>操作のネストが浅いためにラムダの初期プレフィックスが短いことがわかります。覚えておいて、それは

(pure (:) <*> (+3)) <*> ((pure (:) <*> (+2)) <*> pure [])

したがって、外側の(:)深さはラムダの 3 つだけにする必要があります。提案された展開は、おそらく、上記のブラケットが異なるバージョンに対応する可能性があると思います。

pure (:) <*> (+3) <*> pure (:) <*> (+2) <*> pure []

確かに評価すると

putStrLn $ pu "(:)" >*< "(+3)" >*< pu "(:)" >*< "(+2)" >*< pu "[]" ++ " 3 "

私は得る

(\g -> (\g -> (\g -> (\g -> (\_ -> (:)) g ((+3) g)) g ((\_ -> (:)) g)) g ((+2) g)) g ((\_ -> []) g)) 3

(更新された)と一致するように見えます

(\d -> (\c -> (\b -> (\a -> (\_ -> (:)) a ((+3) a)) b ((\_ -> (:)) b)) c ((+2) c)) d ((\_ -> []) d)) 3

この機械による調査が、何が起こっているのかを明らかにするのに役立つことを願っています.

于 2012-08-10T18:05:01.813 に答える
4

(\_ -> (:)) <*> (+3)として書き換えまし\a -> (\_ -> (:)) a (+3) aたが、これはではなくf <*> gとして書き換えています。私はあなたがすべてのためにその間違いをしたと思います.f x g xf x (g x)<*>

于 2012-08-10T17:25:57.200 に答える
2

ラムダ式としての定義ではなく、 や などのコンビネータを記号的_Sに使用する方が簡単かもしれません。_K

_S f g x = f x (g x)
_K x y   = x

他の人がすでに述べたように、関数でfmapは is(.)<*>isです。_Sそう、

sequenceA [(+3),(+2)] 3 ==
    ( ((:) <$> (+3)) <*> sequenceA [(+2)]        ) 3  ==
    _S ((:).(+3))   ( ((:) <$> (+2)) <*> pure [] ) 3  ==
    _S ((:).(+3))   ( _S ((:).(+2))   (_K [])    ) 3  ==
       ((:).(+3)) 3 ( _S ((:).(+2))   (_K [])  3 )    ==
       ((:).(+3)) 3 (    ((:).(+2)) 3 (_K [] 3)  )    ==
    (6:) ( (5:) [] ) ==
    [6,5]

したがって、式を基本的な関数とコンビネータに分解し、そこで停止する (つまり、それらをラムダ式に分解しない) 方が簡単な場合があります式を操作する際に「書き換え規則」を使用して、よりわかりやすい形式を見つけます。

sequenceA必要に応じて、次のように、より抽象的で非公式な書き直し規則を自分で書き留めることができます。

sequenceA [f,g,..., z] ==
    _S ((:).f) . _S ((:).g) . _S ..... . _S ((:).z) . _K []

など

sequenceA [f,g,..., z] a ==
    ((:).f) a $ ((:).g) a $  ..... $ ((:).z) a $ _K [] a ==
    (f a:)    $ (g a:)    $  ..... $ (z a:)    $ []      ==
    [f a, g a, ..., z a]

それゆえ

sequenceA fs a == map ($ a) fs == flip (map . flip ($)) fs a

つまり、

Prelude Control.Applicative> flip (map . flip ($)) [(+3),(+2)] 3
[6,5]
于 2012-08-11T19:51:31.417 に答える