Arrow型クラス
なので、>>>は「computationとcomputationの合成」と言っても、「arrowとarrowの合成」と言っても正しい 全てのモナドはArrowに変換できる
モナドで済む場合は、モナドを使うべき
モナドに似ているが、モナドではないときはArrowを使えばいい
ノリだけ思い出したければこれを読めばいいmrsekut.icon
関数を計算に変換する機能と 2 つの計算を並列に実行する計算を作る機能を兼ね備えたのが Arrow クラス
複数の入力を消費できる
入力値による選択が行える
フィードバックが可能である
定義
code:hs
class Category h => Arrow h where
-- 関数を計算へ持ち上げ
arr :: (a -> b) -> h a b
(***) :: h a b -> h c d -> h (a, c) (b, d)
first :: h a b -> h (a,c) (b,c)
first = (*** id)
second :: h a b -> h (c,a) (c,b)
second = (id ***)
first
first f = f *** idと同じ
よくわかっていないmrsekut.icon
code:hs
first (arr f) = arr (f >< id) -- 拡張
first (f >>> g) = first f >>> first g -- 関手
first f >>> arr (id >< g) = arr (id >< g) >>> first f -- 交換
first f >>> arr fst = arr fst >>> f -- 単位
first (first f) >>> arr assoc = arr assoc first f -- 結合
seocnd
second = (id ***)と同じ
ミニマムな定義
上の定義では、***とsecondも個別に定義しているが、
arrとfirstがあれば実装できる
code:hs
second :: h b c -> h (d, b) (d, c)
seconf f = (arr swap) . (first f) . (arr swap)
where swap ~(x, y) = (y, x)
(***) :: h b c -> h b' c' -> h (b, b') (c, c')
f *** g = (first f) . (second g)
計算に適用する前に入力を複製する
code:hs
(&&&) :: h a b -> h a b' -> h a (b,b')
f &&& g = arr (\b -> (b,b)) >>> f *** g
インスタンス
普通の関数(->)
code:hs
instance Arrow (->) where
arr f = f
(***) f g ~(x,y) = (f x, g y)
code:hs
instance Monad m => Arrow (Kleisli m) where
-- 関数を計算へ持ち上げ
arr :: (a -> b) -> (a -> m b)
arr f = return . f
-- 計算を並列実行する計算を作成
(***) :: (a -> m b) -> (c -> m d) -> ((a, c) -> m (b, d))
f *** g = \(x, y) -> do {s <- f x; t <- g y; return (s, t)}
使用例
普通の関数(->)
code:hs
plus1 = (+1)
double = (*2)
(plus1 *** double) (10,10) -- >> (11,20)
first plus1 (10,10) -- >> (11,10)
second double (10,10) -- >> (10,20)
Arrowを継承した型クラス
いつ嬉しい?
タプルってそんな使わなくね?という気持ちがあるが、そんなに嬉しいのか?
ユースケース
参考
最初に読むのにすごく良い
敢えて関数名とか定義とずらしているんだろうけど、逆に混乱を招く感じもするmrsekut.icon
Categoryの(.)など(記事内では>>>)が微妙に型がズレているので注意mrsekut.icon