データ構造に対する処理を抽象化するための型クラスとして,第3回ではFunctorクラス,第34回ではFoldableクラスを説明しました。FunctorクラスやFoldableクラスでは,「データ構造の走査(traverse)」と「データ構造を走査した際に行う処理」をセットにしたfmapやfold*という処理が直接提供されています。
Haskell以外の言語が持つイテレータなどの機能では「データ構造の走査」と「データ構造を走査した際に行う処理」を切り離すことで,より一般的な処理を行えます。Haskellではそのような枠組みとして,Data.TraversableモジュールでTraversableクラスが提供されています。
mapM関数やsequence関数を一般化
まず,Traversableクラスがどのようなものかを簡単に見てみましょう。
Prelude Data.Traversable> :i Traversable class (Functor t, Data.Foldable.Foldable t) => Traversable t where traverse :: Control.Applicative.Applicative f => (a -> f b) -> t a -> f (t b) sequenceA :: Control.Applicative.Applicative f => t (f a) -> f (t a) Data.Traversable.mapM :: Monad m => (a -> m b) -> t a -> m (t b) Data.Traversable.sequence :: Monad m => t (m a) -> m (t a) -- Defined in Data.Traversable instance Traversable [] -- Defined in Data.Traversable instance Traversable Maybe -- Defined in Data.Traversable
Traversableクラスは,FunctorクラスとFoldableクラスのサブクラスとして定義されています。
TraversableクラスのmapMメソッドの型を見ると,mapMメソッドは第7回で説明したPreludeのmapM関数をTraversableクラスのインスタンスである型に一般化したものであることがわかります。同様に,sequenceメソッドはsequence関数をTraversableクラスのインスタンスである型に一般化したものです。
Prelude> :t mapM mapM :: Monad m => (a -> m b) -> [a] -> m [b] Prelude> :t sequence sequence :: Monad m => [m a] -> m [a] Prelude> :m Data.Traversable Prelude Data.Traversable> :t Data.Traversable.mapM Data.Traversable.mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b) Prelude Data.Traversable> :t Data.Traversable.sequence Data.Traversable.sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
さらに,Traversableクラスでは,mapMのApplicative版であるtraverseメソッドと,sequenceのApplicative版であるsequenceAが提供されています。
Prelude Data.Traversable> :t traverse traverse :: (Traversable t, Control.Applicative.Applicative f) => (a -> f b) -> t a -> f (t b) Prelude Data.Traversable> :t sequenceA sequenceA :: (Traversable t, Control.Applicative.Applicative f) => t (f a) -> f (t a)
Data.Traversableモジュールでは,mapMの引数を入れ替えたforM関数と,traverseの引数を入れ替えたfor関数も用意されています。
-- | 'for' is 'traverse' with its arguments flipped. for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b) {-# INLINE for #-} for = flip traverse -- | 'forM' is 'mapM' with its arguments flipped. forM :: (Traversable t, Monad m) => t a -> (a -> m b) -> m (t b) {-# INLINE forM #-} forM = flip mapM