Язык и библиотеки Haskell 98

Утилиты работы с монадами

Показывать лекцию целиком
module Monad (
    MonadPlus(mzero, mplus),
    join, guard, when, unless, ap,
    msum,
    filterM, mapAndUnzipM, zipWithM, zipWithM_, foldM, 
    liftM, liftM2, liftM3, liftM4, liftM5,

    - ...и то, что экспортирует Prelude
    Monad((>>=), (>>), return, fail),
    Functor(fmap),
    mapM, mapM_, sequence, sequence_, (=<<), 
    ) where

class  Monad m => MonadPlus m  where
    mzero  :: m a
    mplus  :: m a -> m a -> m a

join             :: Monad m => m (m a) -> m a
guard            :: MonadPlus m => Bool -> m ()
when             :: Monad m => Bool -> m () -> m ()
unless           :: Monad m => Bool -> m () -> m ()
ap  :: Monad m => m (a -> b) -> m a -> m b

mapAndUnzipM     :: Monad m => (a -> m (b,c)) -> [a] -> m ([b], [c])
zipWithM         :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m [c]
zipWithM_        :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m ()
foldM            :: Monad m => (a -> b -> m a) -> a -> [b] -> m a
filterM  :: Monad m => (a -> m Bool) -> [a] -> m [a]

msum   :: MonadPlus m => [m a] -> m a

liftM            :: Monad m => (a -> b) -> (m a -> m b)
liftM2           :: Monad m => (a -> b -> c) -> (m a -> m b -> m c)
liftM3           :: Monad m => (a -> b -> c -> d) ->
                               (m a -> m b -> m c -> m d)
liftM4           :: Monad m => (a -> b -> c -> d -> e) ->
                               (m a -> m b -> m c -> m d -> m e)
liftM5           :: Monad m => (a -> b -> c -> d -> e -> f) ->
                               (m a -> m b -> m c -> m d -> m e -> m f)

Библиотека Monad определяет класс MonadPlus и обеспечивает некоторые полезные операции над монадами.

20.1. Соглашения об именах

Функции в этой библиотеке используют следующие соглашения об именах:

  • Суффикс "M" всегда обозначает функцию в категории Клейсли (Kleisli): m добавляется к результатам функции (карринг по модулю) и больше нигде. Так, например,
    filter  ::            (a ->   Bool) -> [a] ->   [a]
      filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
  • Суффикс "_" меняет тип результата (m a) на (m ()). Таким образом (в Prelude):
    sequence  :: Monad m => [m a] -> m [a] 
    sequence_ :: Monad m => [m a] -> m ()
  • Приставка "m" обобщает существующую функцию на монадическую форму. Таким образом, например:
    sum  :: Num a       => [a]   -> a
      msum :: MonadPlus m => [m a] -> m a
  • 20.2. Класс MonadPlus

    Класс MonadPlus определен следующим образом:

    class  Monad m => MonadPlus m  where
        mzero  :: m a
        mplus  :: m a -> m a -> m a

    Методы класса mzero и mplus являются соответственно нулем и плюсом для монады.

    Списки и тип Maybe являются экземплярами класса MonadPlus, таким образом:

    instance  MonadPlus Maybe  where
        mzero                 = Nothing
        Nothing `mplus` ys    = ys
        xs      `mplus` ys    = xs
    
    instance  MonadPlus []  where
        mzero = []
        mplus = (++)

    20.3. Функции

    Функция join является обычным оператором объединения монад. Он используется для того, чтобы убрать один уровень монадической структуры, проектируя его связанный аргумент во внешний уровень.

    Функция mapAndUnzipM устанавливает соответствие (отображает) между своим первым аргументом и списком, возвращая результат в виде пары списков. Эта функция главным образом используется со сложными структурами данных или с монадой преобразований состояний.

    Функция zipWithM обобщает zipWith на произвольные монады. Например, следующая функция выводит на экран файл, добавляя в начало каждой строки ее номер:

    listFile :: String -> IO ()
    listFile nm =
      do cts <- readFile nm
         zipWithM_ (\i line -> do putStr (show i); putStr ": "; putStrLn line)
                   [1..]
                   (lines cts)

    Функция foldM аналогична foldl, за исключением того, что ее результат инкапсулируется в монаде. Обратите внимание, что foldM работает над перечисленными аргументами слева направо. При этом могла бы возникнуть проблема там, где (>>) и "сворачивающая функция" не являются коммутативными.

    foldM f a1 [x1, x2, ..., xm ]
    ==  
        do
          a2 <- f a1 x1
          a3 <- f a2 x2
          ...
          f am xm

    Если требуется вычисление справа налево, входной список следует обратить (поменять порядок элементов на обратный).

    Функции when и unless обеспечивают условное выполнение монадических выражений. Например,

    when debug (putStr "Отладка\n")

    выведет строку "Отладка\n", если булево значение debug равняется True, иначе не выведет ничего.

    Монадическое повышение операторов повышает функцию до монады. Аргументы функции рассматриваются слева направо. Например,

    liftM2 (+) [0,1] [0,2] = [0,2,1,3]
    liftM2 (+) (Just 1) Nothing = Nothing

    Во многих ситуациях операции liftM могут быть заменены на использование ap, которое повышает применение функции.

    return f `ap` x1 `ap` ... `ap` xn

    эквивалентно

    liftMn f x1 x2 ... xn

    20.4. Библиотека Monad

    module Monad (
        MonadPlus(mzero, mplus),
        join, guard, when, unless, ap,
        msum,
        filterM, mapAndUnzipM, zipWithM, zipWithM_, foldM, 
        liftM, liftM2, liftM3, liftM4, liftM5,

    - ...и то, что экспортирует Prelude

    Monad((>>=), (>>), return, fail),
        Functor(fmap),
        mapM, mapM_, sequence, sequence_, (=<<), 
        ) where

    - Определение класса MonadPlus

    class  (Monad m) => MonadPlus m  where
        mzero  :: m a
        mplus  :: m a -> m a -> m a

    - Экземпляры класса MonadPlus

    instance  MonadPlus Maybe  where
        mzero                 = Nothing
    
        Nothing `mplus` ys    =  ys
        xs      `mplus` ys    =  xs
    
    instance  MonadPlus []  where
        mzero =  []
        mplus = (++)

    - Функции

    msum  :: MonadPlus m => [m a] -> m a
    msum xs  =  foldr mplus mzero xs
    
    join             :: (Monad m) => m (m a) -> m a
    join x           =  x >>= id
    
    when             :: (Monad m) => Bool -> m () -> m ()
    when p s         =  if p then s else return ()
    
    unless           :: (Monad m) => Bool -> m () -> m ()
    unless p s       =  when (not p) s
    
    ap               :: (Monad m) => m (a -> b) -> m a -> m b
    ap               =  liftM2 ($)
    
    guard            :: MonadPlus m => Bool -> m ()
    guard p          =  if p then return () else mzero
    
    mapAndUnzipM     :: (Monad m) => (a -> m (b,c)) -> [a] -> m ([b], [c])
    mapAndUnzipM f xs = sequence (map f xs) >>= return . unzip
    
    zipWithM         :: (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m [c]
    zipWithM f xs ys =  sequence (zipWith f xs ys)
    
    zipWithM_         :: (Monad m) => (a -> b -> m c) -> [a] -> [b] -> m ()
    zipWithM_ f xs ys =  sequence_ (zipWith f xs ys)
    
    foldM            :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
    foldM f a []     =  return a
    foldM f a (x:xs) =  f a x >>= \ y -> foldM f y xs
    
    filterM :: Monad m => (a -> m Bool) -> [a] -> m [a]
    filterM p []     = return []
    filterM p (x:xs) = do { b  <- p x;
    ys <- filterM p xs; 
    return (if b then (x:ys) else ys)
       }
    
    liftM            :: (Monad m) => (a -> b) -> (m a -> m b)
    liftM f          =  \a -> do { a' <- a; return (f a') }
    
    liftM2           :: (Monad m) => (a -> b -> c) -> (m a -> m b -> m c)
    liftM2 f         =  \a b -> do { a' <- a; b' <- b; return (f a' b') }
    
    liftM3           :: (Monad m) => (a -> b -> c -> d) ->
                                     (m a -> m b -> m c -> m d)
    liftM3 f         =  \a b c -> do { a' <- a; b' <- b; c' <- c;
       return (f a' b' c') }
    
    liftM4           :: (Monad m) => (a -> b -> c -> d -> e) ->
                                     (m a -> m b -> m c -> m d -> m e)
    liftM4 f         =  \a b c d -> do { a' <- a; b' <- b; c' <- c; d' <- d;
         return (f a' b' c' d') }
    
    liftM5           :: (Monad m) => (a -> b -> c -> d -> e -> f) ->
                                     (m a -> m b -> m c -> m d -> m e -> m f)
    liftM5 f         =  \a b c d e -> do { a' <- a; b' <- b; c' <- c; d' <- d;
           e' <- e; return (f a' b' c' d' e') }
    Вернуться к учебному плану