とは インデックス付きモナドとですか?このモナドの動機は?
私はそれが副作用を追跡するのに役立つことを読みました。しかし、型シグネチャとドキュメンテーションは私をどこにも導きません。
副作用の追跡に役立つ例(または他の有効な例)は何ですか?
とは インデックス付きモナドとですか?このモナドの動機は?
私はそれが副作用を追跡するのに役立つことを読みました。しかし、型シグネチャとドキュメンテーションは私をどこにも導きません。
副作用の追跡に役立つ例(または他の有効な例)は何ですか?
回答:
いつものように、人々が使用する用語は完全に一貫しているわけではありません。モナドに触発されたが厳密に言えば、そうではない概念はさまざまです。「インデックス付きモナド」という用語は、そのような概念の1つを特徴付けるために使用される用語の1つ(「モナディッシュ」と「パラメータ化モナド」(Atkeyの名前))です。(もし興味があれば、そのような別の概念は、勝又の「パラメトリック効果モナド」であり、モノイドによってインデックスが付けられ、戻り値はニュートラルにインデックスされ、バインドはそのインデックスに蓄積されます。)
まずは種類を確認しましょう。
IxMonad (m :: state -> state -> * -> *)
つまり、「計算」のタイプ(または「アクション」、必要に応じて「計算」のみを使用)は、次のようになります。
m before after value
どこbefore, after :: state
とvalue :: *
。このアイデアは、予測可能な状態の概念を持つ外部システムと安全に対話するための手段を取り込むことです。計算のタイプは、実行する必要がある状態before
、実行する状態after
、および(通常のモナドの場合と同様に*
)どのタイプのタイプかを示しますvalue
計算によって生成さます。
通常の小片は*
、モナドのstate
ようであり、ドミノをプレイするのと同様です。
ireturn :: a -> m i i a -- returning a pure value preserves state
ibind :: m i j a -> -- we can go from i to j and get an a, thence
(a -> m j k b) -- we can go from j to k and get a b, therefore
-> m i k b -- we can indeed go from i to k and get a b
このようにして生成された「Kleisli arrow」(計算を生成する関数)の概念は、
a -> m i j b -- values a in, b out; state transition i to j
そして私たちは構図を得ます
icomp :: IxMonad m => (b -> m j k c) -> (a -> m i j b) -> a -> m i k c
icomp f g = \ a -> ibind (g a) f
そして、相変わらず、法律はそれを正確に保証しireturn
、icomp
私たちにカテゴリーを与えます
ireturn `icomp` g = g
f `icomp` ireturn = f
(f `icomp` g) `icomp` h = f `icomp` (g `icomp` h)
または、コメディの偽のC / Java /何でも
g(); skip = g()
skip; f() = f()
{g(); h()}; f() = h(); {g(); f()}
なぜわざわざ?相互作用の「ルール」をモデル化する。たとえば、ドライブにDVDがない場合はDVDを取り出せません。また、ドライブにDVDがすでにある場合は、DVDをドライブに挿入できません。そう
data DVDDrive :: Bool -> Bool -> * -> * where -- Bool is "drive full?"
DReturn :: a -> DVDDrive i i a
DInsert :: DVD -> -- you have a DVD
DVDDrive True k a -> -- you know how to continue full
DVDDrive False k a -- so you can insert from empty
DEject :: (DVD -> -- once you receive a DVD
DVDDrive False k a) -> -- you know how to continue empty
DVDDrive True k a -- so you can eject when full
instance IxMonad DVDDrive where -- put these methods where they need to go
ireturn = DReturn -- so this goes somewhere else
ibind (DReturn a) k = k a
ibind (DInsert dvd j) k = DInsert dvd (ibind j k)
ibind (DEject j) k = DEject j $ \ dvd -> ibind (j dvd) k
これで、「基本的な」コマンドを定義できます
dInsert :: DVD -> DVDDrive False True ()
dInsert dvd = DInsert dvd $ DReturn ()
dEject :: DVDrive True False DVD
dEject = DEject $ \ dvd -> DReturn dvd
そこから他のものはで組み立てられるireturn
とibind
。今、私は書くことができます(借用- do
記法)
discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dvd' <- dEject; dInsert dvd ; ireturn dvd'
しかし、物理的に不可能ではない
discSwap :: DVD -> DVDDrive True True DVD
discSwap dvd = do dInsert dvd; dEject -- ouch!
あるいは、自分のプリミティブコマンドを直接定義することもできます。
data DVDCommand :: Bool -> Bool -> * -> * where
InsertC :: DVD -> DVDCommand False True ()
EjectC :: DVDCommand True False DVD
次に、汎用テンプレートをインスタンス化します
data CommandIxMonad :: (state -> state -> * -> *) ->
state -> state -> * -> * where
CReturn :: a -> CommandIxMonad c i i a
(:?) :: c i j a -> (a -> CommandIxMonad c j k b) ->
CommandIxMonad c i k b
instance IxMonad (CommandIxMonad c) where
ireturn = CReturn
ibind (CReturn a) k = k a
ibind (c :? j) k = c :? \ a -> ibind (j a) k
実質的には、Kleisliの原始的な矢印とは何か(1つの「ドミノ」とは)を述べた後、それらに対して「計算シーケンス」の適切な概念を構築しました。
すべてのインデックス付きモナドm
について、「変化なしの対角線」m i i
はモナドですが、一般的には、m i j
はそうはありません。さらに、値にはインデックスが付けられていませんが、計算にはインデックスが付けられているため、インデックス付きモナドは、他のカテゴリに対してインスタンス化されたモナドの通常のアイデアではありません。
さて、もう一度クライスリ矢のタイプを見てください
a -> m i j b
i
開始するには状態でなければならないことを知っており、継続は状態から開始すると予測しますj
。私たちはこのシステムについてたくさん知っています!これは危険な操作ではありません!ドライブにDVDを入れると、DVDが入ります!dvdドライブは、各コマンドの後の状態について何の発言もしません。
しかし、世界と相互作用するとき、それは一般的に真実ではありません。時には、いくつかのコントロールを提供して、世界に好きなようにさせる必要があるかもしれません。たとえば、サーバーの場合、クライアントに選択肢を提供することができ、セッション状態はクライアントの選択に依存します。サーバーの「オファーの選択」操作は結果の状態を決定しませんが、サーバーはとにかく続行できるはずです。上記の意味での「プリミティブコマンド」ではないため、インデックス付きモナドは予測不可能なモデルを作成するのに適したツールではありませんシナリオ。
より良いツールは何ですか?
type f :-> g = forall state. f state -> g state
class MonadIx (m :: (state -> *) -> (state -> *)) where
returnIx :: x :-> m x
flipBindIx :: (a :-> m b) -> (m a :-> m b) -- tidier than bindIx
怖いビスケット?2つの理由から、そうではありません。一つは、それがあるため、より多くのモナドが何であるかのように見えるというでモナドが、オーバー(state -> *)
ではなく*
。2つ目は、クライスリ矢印のタイプを見ると、
a :-> m b = forall state. a state -> m b state
Good Old Hoare Logicと同様に、前提条件 a
と事後条件を使用して計算のタイプを取得しますb
。プログラムロジックのアサーションは、カリーハワード対応を越えてHaskell型になるまでに半世紀もかかりませんでした。タイプは、returnIx
「何もしないだけで、成立する事後条件を達成できる」と言います。これは、「スキップ」のHoare論理規則です。対応する構成は、「;」のHoare論理規則です。
最後に、のタイプを調べ、bindIx
すべての数量詞を入力します。
bindIx :: forall i. m a i -> (forall j. a j -> m b j) -> m b i
これらforall
の極性は逆です。初期状態i
と、i
事後条件付きで開始できる計算を選択しますa
。世界はj
好きな中間状態を選択しますが、ポストコンディションb
が成り立つという証拠を提供する必要があり、そのようなどの状態からも、持ち続けることができb
ます。したがって、順番に、状態b
から条件を達成できi
ます。「後」の状態を把握することで、予測できない計算をモデル化できます。
どちらIxMonad
とMonadIx
便利です。どちらも、状態の変化に関するインタラクティブな計算の有効性を、それぞれ予測可能および予測不可能にモデル化します。予測可能性は、それを手に入れることができる場合に価値がありますが、予測不可能性は、時には現実の事実です。うまくいけば、この回答はインデックス付きモナドが何であるかをいくつか示し、それらがいつ有用になり始めるか、いつ停止するかを予測します。
DataKinds
拡張機能を使用して可能であり、依存型付き言語でも可能です...まあ、それは一種のことです。
MonadIx
例を挙げて、について少し詳しく説明していただけますか?理論的根拠の方が良いのか、それとも実用的なアプリケーションの方が良いのか?
RebindableSyntax
拡張子付きの有効な構文であると言うと役立つ場合があります。前述のように、他の必要な拡張子の言及は、いいだろうDataKinds
私が知っているインデックス付きモナドを定義するには、少なくとも3つの方法があります。
私はこれらのオプションをXのインデックス付きモナドと呼びます。Xは、コンピューターサイエンティストのBob Atkey、Conor McBride、およびDominic Orchardの範囲です。これらの構造の一部は、カテゴリ理論を通じて、はるかに長い輝かしい歴史とより良い解釈を持っていますが、最初にこれらの名前に関連付けられていることを知り、この答えが難解になりすぎないようにしています。
Bob Atkeyのインデックス付きモナドのスタイルは、モナドのインデックスを処理するために2つの追加パラメーターを使用することです。
これで、他の回答で人々が投げかけた定義がわかります。
class IMonad m where
ireturn :: a -> m i i a
ibind :: m i j a -> (a -> m j k b) -> m i k b
Atkeyのインデックス付きコマンドも定義できます。私は実際にそれらのうち、走行距離の多くを得るでlens
コードベース。
インデックス付きモナドの次の形式は、コナーマクブライドの論文「Kleisli Arrows of Outrageous Fortune」からの定義です。代わりに、インデックスに単一のパラメーターを使用します。これにより、インデックス付きモナド定義はかなり賢い形になります。
次のようにパラメトリック性を使用して自然変換を定義すると、
type a ~> b = forall i. a i -> b i
次に、マクブライドの定義を次のように書き留めます。
class IMonad m where
ireturn :: a ~> m a
ibind :: (a ~> m b) -> (m a ~> m b)
これはAtkeyのものとはかなり違っていますが、通常のモナドのように感じ(m :: * -> *)
られ(m :: (k -> *) -> (k -> *)
ます。モナドをで構築するのではなく、で構築します。
興味深いことに、賢明なデータ型を使用することにより、実際にAtkeyのインデックス付きモナドのスタイルをMcBrideのスタイルから回復できます。
data (:=) :: a i j where
V :: a -> (a := i) i
今、あなたはそれを解決することができます
ireturn :: IMonad m => (a := j) ~> m (a := j)
に拡大する
ireturn :: IMonad m => (a := j) i -> m (a := j) i
j = iの場合にのみ呼び出すことができ、を注意深く読むとibind
Atkeyと同じように戻すことができますibind
。これらの(:=)データ構造を渡す必要がありますが、Atkeyプレゼンテーションの力を回復します。
一方、Atkeyのプレゼンテーションは、McBrideのバージョンのすべての使用を回復するほど強力ではありません。力は厳しく獲得されました。
もう1つの良い点は、マクブライドのインデックス付きモナドが明らかにモナドであり、それが別のファンクターカテゴリのモナドにすぎないことです。これは、からへのファンクタのカテゴリではなく、から(k -> *)
へのファンクタのカテゴリで内部(k -> *)
ファンクタ*
に対して機能し*
ます。
楽しい演習では、インデックス付きコモナードの McBrideからAtkeyへの変換方法を考え出します。私はマクブライドの論文の「アットキー」構造に「At」というデータタイプを個人的に使用しています。私は実際にICFP 2013でボブ・アットキーに近づき、彼を裏返しにして彼を「コート」にしたと述べました。彼は目に見えて邪魔をしているようだった。頭の中でラインがうまく流れました。=)
最後に、「インデックス付きモナド」という名前のあまり一般的ではない3番目の主張者は、ドミニクオーチャードによるもので、代わりにタイプレベルのモノイドを使用してインデックスをつぶします。構造の詳細を確認するのではなく、単にこの話にリンクします。
https://github.com/dorchard/effect-monad/blob/master/docs/ixmonad-fita14.pdf
ibind
、型エイリアスを紹介しAtkey m i j a = m (a := j) i
ます。これを使用してm
、我々が検索Atkeyの定義が回復における2つのシグネチャ:ireturnAtkin :: a -> m (a := i) i
とibindAtkin :: m (a := j) i -> (a -> m (b := k) j) -> m (b := k) i
。最初のものは、合成によって得られますireturn . V
。2番目の方法は、(1)forall j. (a := j) j -> m (b := k) j
パターンマッチングによって関数を作成し、復元a
したものをの2番目の引数に渡しますibindAtkin
。
簡単なシナリオとして、状態モナドがあると仮定します。状態タイプは複雑で大きなものですが、これらの状態はすべて2つのセット(赤と青の状態)に分割できます。このモナドの一部の操作は、現在の状態が青の状態である場合にのみ意味があります。これらの中には、状態を青(blueToBlue
)のままにするものもあれば、状態を赤(blueToRed
)にするものもあります。通常のモナドでは、
blueToRed :: State S ()
blueToBlue :: State S ()
foo :: State S ()
foo = do blueToRed
blueToBlue
2番目のアクションは青色の状態を想定しているため、ランタイムエラーをトリガーします。これを静的に防止したいと思います。インデックス付きモナドはこの目標を満たします。
data Red
data Blue
-- assume a new indexed State monad
blueToRed :: State S Blue Red ()
blueToBlue :: State S Blue Blue ()
foo :: State S ?? ?? ()
foo = blueToRed `ibind` \_ ->
blueToBlue -- type error
第2のインデックスが原因タイプエラーがトリガされblueToRed
(Red
)の最初のインデックスと異なりますblueToBlue
(Blue
)。
別の例として、インデックス付きモナドを使用すると、状態モナドがその状態のタイプを変更できるようにすることができます。たとえば、
data State old new a = State (old -> (new, a))
上記を使用して、静的に型付けされた異種スタックである状態を構築できます。操作にはタイプがあります
push :: a -> State old (a,old) ()
pop :: State (a,new) new a
別の例として、ファイルアクセスを許可しない制限付きIOモナドが必要だとします。あなたは例えば
openFile :: IO any FilesAccessed ()
newIORef :: a -> IO any any (IORef a)
-- no operation of type :: IO any NoAccess _
このようにして、タイプを持つアクションは、IO ... NoAccess ()
ファイルアクセスフリーであることが静的に保証されます。代わりに、タイプのアクションはIO ... FilesAccessed ()
ファイルにアクセスできます。インデックス付きモナドがあると、制限されたIOに個別のタイプを構築する必要がなくなり、両方のIOタイプでファイルに関連しないすべての関数を複製する必要があります。
インデックス付きモナドは、たとえば、状態モナドのような特定のモナドではなく、追加の型パラメーターを持つモナド概念の一種の一般化です。
「標準」モナディック値が型を持つのに対し、Monad m => m a
インデックス付きモナドの値はIndexedMonad m => m i j a
どこにi
ありj
、インデックス型であるためi
、モナディック計算の開始時と計算j
終了時のインデックスの型になります。ある意味でi
は、一種の入力タイプおよびj
出力タイプと考えることができます。
使用State
例として、ステートフルな計算は、State s a
型の状態を維持しs
、計算全体およびタイプの結果を返しますa
。インデックス付きバージョンIndexedState i j a
はステートフルな計算であり、計算中に状態が別のタイプに変化する可能性があります。初期状態にはタイプi
と状態があり、計算の終わりにはタイプがありj
ます。
通常のモナドに対してインデックス付きモナドを使用する必要はめったにありませんが、場合によっては、より厳密な静的保証をエンコードするために使用できます。
依存型(agdaなど)でのインデックスの使用方法を確認することが重要な場合があります。これは、一般的にインデックス作成がどのように役立つかを説明し、この経験をモナドに変換できます。
索引付けにより、タイプの特定のインスタンス間の関係を確立できます。次に、いくつかの値について推論して、その関係が成り立つかどうかを確認できます。
たとえば(agdaで)いくつかの自然数がと関連していることを指定でき_<_
、型はそれらがどの数であるかを示します。次にm < n
、関数が正しく機能するため、一部の関数にの目撃情報を与えることを要求できます。そのような目撃情報を提供しないと、プログラムはコンパイルされません。
別の例として、選択した言語に対する十分な忍耐力とコンパイラーのサポートが与えられた場合、関数が特定のリストがソートされていると想定するようにエンコードできます。
インデックス付きモナドでは、依存型システムが行うことの一部をエンコードして、副作用をより正確に管理できます。
True
/False
値を型引数としてどのように渡すことができますDVDDrive
か?それはいくつかの拡張機能ですか、またはブール値が実際にここに入力されていますか?