データ構造に対する処理を抽象化するための型クラスとして,第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