KleisliでもArrowしたい。
Haskellの話。
Control.Arrow
というライブラリがあるのだが、自分に取っては
first, second, (***),(&&&)くらいを使うくらいのユーティリティライブラリでしかなかった。
class Category cat where id :: cat a a (.) :: cat b c -> cat a b -> cat a c (>>>) :: Category cat => cat a b -> cat b c -> cat a c a >>> b = b . a class Category a => Arrow a where arr :: (b -> c) -> a b c first :: a b c -> a (b,d) (c,d) second :: a b c -> a (d,b) (d,c) (***) :: a b c -> a b' c' -> a (b,b') (c,c') (&&&) :: a b c -> a b c' -> a b (c,c')
(>>>)は射の合成、first,secondはそれぞれペアの第1要素、第2要素のみを変更する射、
(***)は射の積、(&&&)は一つのソースを二つに分割する射となっている。
最近モナドでfirst, secondを使いたくなるときがあったのでメモ。
具体的には次のようなコードだ
splitBy :: [a] -> Int -> [[a]] splitBy [] _ = [] splitBy l n = hd:splitBy tl n where (hd,tl) = splitAt n l
これはリーダーモナドを使って次のように書ける。
splitBy :: [a] -> Int -> [[a]] splitBy [] = return [] splitBy l = do (hd,tl) <- flip splitAt l tl' <- splitBy tl return (hd:tl')
気持ち的には
splitBy l = fmap (uncurry (:)) $ flip splitBy l >>= second splitBy
のような感じで書きたいのだがsecondの型が合わない。
調べてみると
newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b } instance Monad m => Arrow (Kleisli m)
というinstanceを見つけたのでこれが使えそう。
splitBy :: [a] -> Int -> [[a]] splitBy [] = return [] splitBy l = fmap (uncurry (:)) $ flip splitAt l >>= runKleisli (second (Kleisli splitBy))
これで型もあうのだが、>>=の右側がどうにも格好悪い。
他の例も考えてみてよい補助関数を見つけたいところだ。