(NBこれは、両方の鉱山から、上ガブリエルのコメント@ビットを兼ね備えています。)
をFunctor
のFix
エド・ポイントのすべての住民が無限であるためにはそれが可能だ、つまりlet x = (Fix (Id x)) in x === (Fix (Id (Fix (Id ...))))
はのだけ住人でありますFix Identity
。 Free
は、少なくとも有限住人Free f
が確実に存在する点で、Fix
とすぐに異なります。実際、Fix f
に無限の住人がある場合、Free f
には無限に多くの有限の住人がいます。
この無限のもう一つの即時の副作用は、Functor f => Fix f
がもうFunctor
ではないということです。 fmap :: Functor f => (a -> b) -> (f a -> f b)
を実装する必要がありますが、Fix
にはa
を含むf a
の「すべての穴がいっぱいです」というメッセージが含まれているため、 '関数にはa
が適用されなくなりました。
我々はreturn :: a -> Free f a
を実装すると言う、この法律はfmap f . return = return . f
を保持する、したいのですが、それもFunctor f => Fix f
に意味がありませんので、これはMonad
Sを作成するために重要です。
どのようにFree
はこれらを「修正」しますか?Fix
エドポイントはありますか? Pure
コンストラクタを使用して、ベースファンクタを「追加」します。従って、すべてについてFunctor f
、Pure :: a -> Free f a
。これは私たちが保証するタイプの有限の住人です。また、すぐに、return
のよく振る舞う定義を与えてくれます。
return = Pure
だからFix
によって作成されたネストされたFunctor
秒の潜在的に無限の「木」を取り出してPure
に代表される「リビング」芽、いくつかの数で混合としてこのほか考えるかもしれません。 return
を使用して新しい芽を作成します。これは、後でその芽に「戻って」計算を追加するという約束として解釈される可能性があります。実際には、これはまさにflip (>>=) :: (a -> Free f b) -> (Free f a -> Free f b)
の機能です。タイプa
に適用できる「継続」関数f :: a -> Free f b
が与えられた場合、我々はそれぞれPure a
に戻り、f a
として計算された継続と置き換えてツリーを再帰します。これは私たちの木を "成長"させます。
は今、Free
は明らかにFix
よりも一般的です。この家を運転するには、タイプFunctor f => Fix f
を対応するFree f a
のサブタイプとして見ることができます。 data Void = Void Void
(つまり、作成できないタイプ、空のタイプ、インスタンスがないタイプ)のa ~ Void
を選択してください。
は、私たちが
break :: Fix f -> Free f a
で私たちの
Fix
D」
Functor
秒を破ると、その後
affix :: Free f Void -> Fix f
でそれを反転しようとすることができ、それがより明確にします。
break (Fix f) = Free (fmap break f)
affix (Free f) = Fix (fmap affix f)
注は、まずそのaffix
があるため、この場合x :: Void
でPure x
ケースを処理する必要はありませんので、が本当にが存在しないので、Pure x
は不合理であり、我々はそれを無視します。
もa
タイプが唯一それがbreak
の任意のユーザに対して完全にアクセスできないだように、戻り値の型、Free f a
に表示されますので、break
の戻り値の型は少し微妙であることに注意してください。 「完全にアクセスできない」、「インスタンス化できません」は、タイプにかかわらず、affix
とbreak
が逆であるという最初のヒントを与えますが、われわれはそれを証明することができます。 (break . affix)
が同一であることを(共誘導、または単に直感的、おそらく)を示すべき
(break . affix) (Free f)
=== [definition of affix]
break (Fix (fmap affix f))
=== [definition of break]
Free (fmap break (fmap affix f))
=== [definition of (.)]
Free ((fmap break . fmap affix) f)
=== [functor coherence laws]
Free (fmap (break . affix) f)
。他の方向は完全に同一のやり方で進む。
これは、Free f
がFix f
すべてがFunctor f
であることを示しています。
だからなぜFix
を使用しますか?場合によっては、f
のレイヤーの副作用のために、Free f Void
のプロパティのみが必要な場合があります。この場合、Fix f
と呼び出すと、タイプ上で(>>=)
またはfmap
を試してはいけないことがより明確になります。さらにFix
はnewtype
なので、意味的役割しか果たさないので、コンパイラがFix
の層をコンパイルする方が簡単かもしれません。
- 注:私たちは、より正式に
Void
とforall a. a
がより明確にaffix
とbreak
の種類が調和しているかを確認するために、同型のタイプであるかについて話すことができます。たとえば、absurd :: Void -> a
はabsurd (Void v) = absurd v
、unabsurd :: (forall a. a) -> Void
はunabsurd a = a
となります。しかし、これらはちょっとばかげている。
モナドインスタンスの主な利点は、( 'replicateM_'および実施例で' forM_'等) 'Control.Monad'からコンビネータの' do'表記と再利用です。一般的なやり方は、フリーモナドを使用して型を構築することですが、その結果に関数型の固定点に変換できるように 'FreeT fm Void'型を要求します。 –
'Monad'インスタンスは、' return'からの明確な終了と '(>> =)'からの自然な "拡張"という2つのもので 'Fix'を強化します。通常の ''(Fix f) ''は(有限の)値を持つことは保証されませんが、 '' Free f '''には少なくとも '' Pure''が常駐します。 –
@GabrielGonzalezコメントの後半部分について詳しく説明できますか?ユースケースの例は何ですか? –