Spec-Zone.ru › Haskell 7

Control.Monad.Trans.Класс

Авторские права (c) Andy Gill 2001, (c) Oregon Graduate Institute of Science and Technology, 2001
Лицензия BSD-стиль (см. файл LICENSE)
Поддержка ross@soi.city.ac.uk
Стабильность экспериментальная
Переносимость переносимая
Безопасный Haskell Безопасный
Язык Haskell98

Содержание

  • Класс трансформаторов
  • Конвенции
  • Строгие монады
  • Примеры
    • Разбор
    • Разбор и подсчёт
    • Монада интерпретатора

Описание

Класс монадических трансформаторов.

Монадический трансформатор создаёт новую монаду из существующей монады, таким образом, что вычисления старой монады могут быть встроены в новую. Для создания монады с желаемым набором функций обычно начинают с базовой монады, такой как Identity, [] или IO, и применяют последовательность монадических трансформаторов.

Класс трансформаторов

class MonadTrans t where Источник

Класс монадических трансформаторов. Экземпляры должны удовлетворять следующим законам, которые утверждают, что lift является трансформацией монады:

  • lift . return = return
  • lift (m >>= f) = lift m >>= (lift . f)

Методы

lift :: Monad m => m a -> t m a Источник

Поднимает вычисление из аргументной монады в созданную монаду.

Экземпляры

MonadTrans IdentityT
MonadTrans ListT
MonadTrans MaybeT
MonadTrans (ContT r)
MonadTrans (ReaderT r)
MonadTrans (StateT s)
MonadTrans (StateT s)
MonadTrans (ExceptT e)
Error e => MonadTrans (ErrorT e)
Monoid w => MonadTrans (WriterT w)
Monoid w => MonadTrans (WriterT w)
Monoid w => MonadTrans (RWST r w s)
Monoid w => MonadTrans (RWST r w s)

Конвенции

Большинство модулей монадических трансформаторов включают особый случай применения трансформатора к Identity. Например, State s является сокращением для StateT s Identity.

Каждый монадический трансформатор также поставляется с операцией runXXXT для разворачивания трансформатора, предоставляя вычисление внутренней монады. (В настоящее время эти функции определены как метки полей, но в следующей основной версии они будут отдельными функциями.)

Все монадические трансформаторы, кроме ContT, являются функторами на категории монад: помимо определения отображения монад, они также определяют отображение трансформаций между базовыми монадами на трансформации между преобразованными монадами, называемые mapXXXT. Таким образом, заданной монадической трансформацией t :: M a -> N a, комбинатор mapStateT создаёт монадическую трансформацию

mapStateT t :: StateT s M a -> StateT s N a

Каждый из монадических трансформаторов вводит соответствующие операции. В последовательности монадических трансформаторов большинство из этих операций можно поднять через другие трансформаторы с использованием lift или комбинатора mapXXXT, но некоторые с более сложными сигнатурами типов требуют специализированных комбинаторов подъёма, называемых liftOp.

Строгие монады

Монада называется строгой, если её операция >>= является строгой по первому аргументу. Базовые монады Maybe, [] и IO являются строгими:

>>> undefined >> return 2 :: Maybe Integer
*** Exception: Prelude.undefined

Однако монада Identity не является таковой:

>>> runIdentity (undefined >> return 2)
2

В строгой монаде вы знаете, когда выполняется каждое действие, но монада не обязательно строга в возвращаемом значении или в других компонентах монады, таких как состояние. Однако можно использовать seq для создания действия, строгими по компоненту, который требуется вычислить.

Примеры

Разбор

Можно определить монаду разбора, добавив состояние (String остаток для разбора) к монаде [], которая обеспечивает недетерминизм:

import Control.Monad.Trans.State

type Parser = StateT String []

Тогда Parser является экземпляром MonadPlus: монадическая последовательность реализует конкатенацию парсеров, а mplus обеспечивает выбор. Для использования парсеров нужен примитив для запуска созданного парсера на строке ввода:

runParser :: Parser a -> String -> [a]
runParser p s = [x | (x, "") <- runStateT p s]

Наконец, нужен примитивный парсер, который сопоставляет один символ, из которого могут быть построены произвольно сложные парсеры:

item :: Parser Char
item = do
    c:cs <- get
    put cs
    return c

В этом примере используются операции get и put из Control.Monad.Trans.State, которые определены только для монад, являющихся приложениями StateT. В качестве альтернативы можно использовать классы монад из пакета mtl или аналогичного, содержащие методы get и put со обобщёнными типами для всех подходящих монад.

Разбор и подсчёт

Можно определить парсер, который также считает, добавив трансформатор WriterT:

import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import Control.Monad.Trans.Writer
import Data.Monoid

type Parser = WriterT (Sum Int) (StateT String [])

Функция, которая применяет парсер, теперь должна разворачивать каждый монадический трансформатор по очереди:

runParser :: Parser a -> String -> [(a, Int)]
runParser p s = [(x, n) | ((x, Sum n), "") <- runStateT (runWriterT p) s]

Для определения парсера item нужно поднять операции StateT через трансформатор WriterT.

item :: Parser Char
item = do
    c:cs <- lift get
    lift (put cs)
    return c

В этом случае это удалось сделать с помощью lift, но операции с более сложными типами требуют специальных функций подъёма, которые предоставляются монадическими трансформаторами, для которых они могут быть реализованы. Если вы используете классы монад из пакета mtl или аналогичного, этот подъём обрабатывается автоматически экземплярами классов, и вам нужно использовать только обобщённые методы get и put.

Можно также определить примитив с использованием Writer:

tick :: Parser ()
tick = tell (Sum 1)

Тогда парсер будет отслеживать, сколько tick он выполняет.

Монада интерпретатора

Этот пример является сокращённой версией примера в "Моноидные трансформаторы и модульные интерпретаторы", Sheng Liang, Paul Hudak и Mark Jones в POPL'95 (http://web.cecs.pdx.edu/~mpj/pubs/modinterp.html).

Предположим, что нужно определить интерпретатор, который может выполнять I/O, имеет исключения, среду и изменяемое хранилище. Можно определить монаду, которая поддерживает все эти вещи, как стек монадических трансформаторов:

import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import qualified Control.Monad.Trans.Reader as R
import qualified Control.Monad.Trans.Except as E

type InterpM = StateT Store (R.ReaderT Env (E.ExceptT Err []))

для подходящих типов Store, Env и Err.

Теперь нам нужно использовать операции, связанные с каждым из этих монадических трансформаторов, на действиях InterpM. Поскольку верхний монадический трансформатор InterpM является StateT, у него уже есть операции состояния get и set.

Первая из операций ReaderT, ask, — это простое действие, поэтому можно поднять его через StateT в InterpM с помощью lift:

ask :: InterpM Env
ask = lift R.ask

Другая операция ReaderT, local, имеет подходящий тип для подъёма с помощью mapStateT:

local :: (Env -> Env) -> InterpM a -> InterpM a
local f = mapStateT (R.local f)

Также нужно поднять операции ExceptT через ReaderT и StateT. Для операции throwE известно, что throwE e — это простое действие, поэтому можно поднять его через два монадических трансформатора в InterpM с двумя lift:

throwE :: Err -> InterpM a
throwE e = lift (lift (E.throwE e))

Операция catchE имеет более сложный тип, поэтому нам нужно использовать специальную функцию подъёма liftCatch, предоставляемую большинством трансформаторов монады. Здесь мы используем версию ReaderT, за которой следует версия StateT:

catchE :: InterpM a -> (Err -> InterpM a) -> InterpM a
catchE = liftCatch (R.liftCatch E.catchE)

Мы могли бы поднять действия IO в InterpM, используя три lift, но InterpM автоматически является экземпляром MonadIO, поэтому мы можем использовать liftIO вместо этого:

putStr :: String -> InterpM ()
putStr s = liftIO (Prelude.putStr s)

© The University of Glasgow and others
Licensed under a BSD-style license (see top of the page).
https://downloads.haskell.org/~ghc/7.10.3/docs/html/libraries/transformers-0.4.2.0/Control-Monad-Trans-Class.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API