traverse
Traversable型クラスのmethodの1つ
default定義として、よくtraverseが定義される
mapMのApplicative版
mapMの一般化
定義
code:hs
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
traverse f = sequenceA . fmap f
データ構造tと計算コンテナfを入れ替えている
a -> f bにt aを適用するとt (f b)になるはずだが、
結果は、データ構造tと計算コンテナfを入れ替えて、f (t b)になる
Applicative Styleとの関連性
上記の定義をListに対して書き換えてみると以下のように書ける
code:hs
traverse f [] = pure []
traverse f (x:xs) = (:) <$> f x <*> traverse f xs
Applicative Styleとmap的な再帰をしてるのがわかる
逆に言えば、mapっぽい処理をしたいのに、Applicative Styleが出てきちゃうよ〜、という時にtraverseがハマる
e.g. 2023/8/13 DNA
いくつかの型での定義を見る
List
code:hs
traverse :: Applicative f => (a -> f b) -> a -> f b
traverse f [] = pure []
traverse f (x:xs) = (:) <$> f x <*> traverse f xs
実際はfoldrを使ってもっとスマートに定義されているが同じ意味
code:hs
traverse f = foldr (\x ys -> (:) <$> f x <*> ys) (pure [])
Maybe
code:hs
traverse :: Applicative f => (t -> f a) -> Maybe t -> f (Maybe a)
traverse _ Nothing = pure Nothing
traverse f (Just x) = Just <$> f x
Either
code:hs
traverse :: Applicative f => (t -> f b) -> Either a t -> f (Either a b)
traverse _ (Left x) = pure (Left x)
traverse f (Right y) = Right <$> f y
どれも以下のような感じで定義されていることがわかる
code:hs
traverse f ... = pure データ構成子
traverse f ... = データ構成子 <$> f x1 (または traverse f xs1)
<*> f x2 (または traverse f xs2)
...
<*> f xn (または traverse f xsn)
使用例
なんかしょうもない例しか思いつけないが
与えられた[Int]の要素が全部奇数ならばJust [Int]、一つでも偶数があればNothingを返す
code:hs
f :: (Traversable t, Integral b) => t b -> Maybe (t b)
f = traverse $ \n -> if odd n then Just n else Nothing
f 1,2,3 -- Nothing
f 1,3,5 -- Just 1,3,5
参考
第52回 データ構造を走査するためのTraversableクラス(3ページ目) | 日経クロステック(xTECH)
https://publish.obsidian.md/naoya/traverse
https://zenn.dev/gakuzzzz/articles/81cd723a36067f
https://gakuzzzz.github.io/slides/controls_the_traverse_controls_the_code/#1