2017-01-06 6 views
0

魚オペレーターに対して、モナドは連合性を満たす。次の式を意味ラムダ式のパラメータについて、モナドに渡すことが証明できるものは何ですか?

(ラムダ式で)バインドのように見えるように変換
(h >=> g) >=> f = h >=> (g >=> f) 

これ、

\a -> h a >>=(\b -> g b >>= \c -> f c) = 
\a ->(h a >>= \b -> g b)>>= \c -> f c 

これは単項構図を理解するための良い方法である

(a -> h a >>= \b -> g b >>= \c -> f c) = h >=> g >=> f 

曖昧でありません。

ただし、すべてのMonadicコードがラムダへのバインドされた変数を分離しないようにしているわけではありません。例えば、

[1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch) = 
[(1,'a'),(1,'b'),(2,'a'),(2,'b')] 

トップラムダから「n」が得られました。

は、より一般的には、

a -> g a >>= \b -> f a b 

fは上記でAとBの両方に依存します。上記のf、g、(> =>)の定義は、

\a -> (\x -> g a) >=> f a 

です。これはよく分かりません。上記の方程式と一致しません。私はここで基本的な概念として魚を参照し、私はそれが私が書いている典型的なMonadsとどのように相互作用するのかを理解しようとしています。上記をより良く理解したいと思います。これを近づいての

一つの方法は、私は、これは対称性のようなものを意味だと思うリストの表現の構文

[ (n,ch) | n <- [1, 2], ch <- ['a', 'b'] ] 

から意味を取得しようとしていることです。

ラムダ式とMonadsを結ぶ素晴らしい対称はありますか?それともあまりにもこれを読んでいるのですか?非常にネストされたラムダ式の私の恐怖は間違っているか合理的ですか?

+2

私はこの質問を全く理解していません。 – melpomene

+0

"直感的に、私はネストされたラムダ間の微妙な結合を恐れています。" - あなたが心配していることについてもう少し言っていただけますか? – duplode

+0

@duplode私の編集は私の心配を明確にしていますか? – Polymer

答えて

5

いいえ、制限はありません。ラムダにバインドしたら、anythingを実行できます。これはApplicativeの理由のうちの1つで、Monadの方が弱いので、であるため、より強力なフリー定理の制限が得られます。

([1,2] >>= \n -> "ab" >>= \ch -> return (n,ch)) 
    ≡ (,) <$> [1,2] <*> "ab" 
    ≡ liftA2 (,) [1,2] "ab" 
    ≈ liftA2 (flip (,)) "ab" [1,2] 

最後は実際には適切な式ではありません。適用法だけの値はこれらの式のために同じであることを保証しますを参照してください。の構造は異なる可能性があります。 (>=>)を使用して... ...

\a -> g a >>= \b -> f a b 

をあなたが書く方法を検討しているあなたの編集を、アドレッシング

Prelude Control.Applicative> liftA2 (,) [1,2] "ab" 
[(1,'a'),(1,'b'),(2,'a'),(2,'b')] 
Prelude Control.Applicative> liftA2 (flip (,)) "ab" [1,2] 
[(1,'a'),(2,'a'),(1,'b'),(2,'b')] 
+0

正直なところ、 'f <$> x <*> y'と' flip f <$> y <*> x'の間の適用関係がどういう意味を持つのかは分かりません。私は、彼らがその対称性に関して何かを言っているとはかなり確信していますが、私はいつもそれらの法律を正しく理解するのに問題がありました。 – leftaroundabout

+2

あなたのコメントについて: '状態'は 'f <$> x <*>y'と' flip f <$>y <*>x'の間の値の同一性の反例です。例えば 'runState((/)<$>状態((2 * )&&&(3 *))<*>状態((+4)&&&(減算3)))1は '(1/7、0)'ですが、 'runState(flip(/)<$>状態((+4)&&& (減算3))<*>状態((2 *)&&&(3 *)))1は '(-4/5、-6)'です。 – duplode

+1

もっと一般的には、 'Applicative'インターフェースは、それ自体が効果の間の依存関係を有効にしませんが、その存在はそのような依存を妨げません。 StateTは実際には任意の依存関係を許すので、これはStateよりも優れていると思います。 – dfeuer

2

、何も実際にそのような場合には失われることはありません。それが戻って一歩を踏み出すと(>=>)(>>=)およびその逆に変換することができます正確にどのように考慮することが有用である:

f >=> g = \x -> f x >>= g 
m >>= f = (const m >=> f)() -- const x = \_ -> x 

あなたの懸念に関連する1つである第2式では、我々は最初の引数を回します(>>=)constを使用して(>=>)に渡すことができる関数に変換します。 const m >=> fはその引数を無視する関数なので、(>>=)を回復するためには、仮引数として()を渡すだけです。

\a -> g a >>= \b -> f a b 
\a -> (const (g a) >=> \b -> f a b)() 
\a -> (const (g a) >=> f a)() 

ダミー()を供給する追加トリックを除いて、あなたはあなたの質問で得られたものである、:ので、あなたの例では、第2式を使用して書き換えることができますされていること

2

あなたの疑問にお答えください。モナドは、影響が入力に依存するという意味で最も一般的です。入力がaで、出力がbであるモナド計算mは、a -> m bと書くことができます。これは関数なので、lambdaを使ってそのような計算を定義することができます。しかし、この一般性は計算をあなたの\a -> g a >>= \b -> f a bとして解析するのを複雑にします。

arrows(アプリケーションファンクタとモナドの間のスペースを占める)については、状況が多少異なります。一般的な矢印の場合、入力は明示的でなければなりません。矢印の計算arrは一般タイプがarr a bです。矢印計算で「前方」にまたがる入力は、矢印プリミティブを使用して明示的にスレッド化されていなければなりません。 (矢印は一つの入力値を受け入れるように定義されているため)機能fは現在、その入力としてペアを取る必要があります。矢印に

{-# LANGUAGE Arrows #-} 

import Control.Arrow 

bind2 :: (Monad m) => (a -> m b) -> (a -> b -> m c) -> a -> m c 
bind2 g f = \a -> g a >>= \b -> f a b 

あなたの例を拡大して

。矢印do記法を用いて、我々はArrowプリミティブを使用して

bind2A :: (Arrow arr) => arr a b -> arr (a, b) c -> arr a c 
bind2A g f = proc a -> do 
       b <- g -< a 
       c <- f -< (a, b) 
       returnA -< c 

あるいは単純としてそれを表現することができます:グラフィカル

bind2A' :: (Arrow arr) => arr a b -> arr (a, b) c -> arr a c 
bind2A' g f = (returnA &&& g) >>> f 

--------------->[ ] 
    \   [ f ]----> 
    \-->[ g ]-->[ ] 

あまり一般的なので、矢印はより多くを推測することができます実際に実行される前の回路に関する情報良いものはUnderstanding arrowsで、静的な部分と動的な部分を持つことでスペースリークを避けることができるパーサを構築するという、元の動機を示しています。

関連する問題