Работа над перестановочным стеком трансформатора монады
Одна из проблем с монадными трансформаторами, которые я нахожу, - это необходимость lift
операций в правую монаду. Единственный lift
здесь и там неплох, но иногда есть функции, которые выглядят так:
fun = do
lift a
lift b
c
lift d
lift e
f
Я хотел бы написать эту функцию таким образом:
fun = monadInvert $ do
a
b
lift c
d
e
lift f
Это уменьшает число lift
и очищает код.
Вопрос: для каких монад возможно monadInvert
? Как создать эту функцию?
Бонусные очки: определите его для monad m
, который является экземпляром MonadIO
.
Название этого вопроса говорит о перестановках: действительно, как мы можем иметь дело с произвольными перестановками стека трансформатора монады?
Ответы
Ответ 1
Возможно, вас заинтересует Monads, Zippers и Views, виртуализация стека Monad от Tom Schrijvers и Bruno Oliveira.
Это не касается вашей точки зрения о сокращении лифтов, но это интересный подход к вашей проблеме переходов монады.
Здесь реферат:
Эта работа направлена на то, чтобы сделать монолитные компоненты более многоразовыми и надежными к изменениям, используя два новых метода виртуализации монады stack: monad zipper и monad views. Монадная молния - монада трансформатор, который создает виртуальные стеки монады, игнорируя конкретные слоев в бетонном стеке. Представления Monad обеспечивают общую структуру для виртуализации стека монады: они берут застежку-молнию монады на один шаг далее и интегрировать его с широким спектром других виртуализаций. Например, определенные виды разрешают ограниченный доступ к монадам в стек. Кроме того, представления монады могут использоваться компонентами для предоставить механизм вызова по ссылке для доступа к определенным слоям из стека монады. С этими двумя механизмами требования к компонентам в плане формы стека монады больше не нужно быть буквально отражается в бетонном стеке монады, делая эти компоненты более многоразовые и надежные для изменений.
Ответ 2
Ну, во-первых, вам действительно не нужно столько подъема. Для монадных трансформаторов имеют место следующие тождества:
lift c >>= lift . f = lift (c >>= f)
lift c1 >> lift c2 = lift (c1 >> c2)
Это не редкость писать:
x <- lift $ do
{- ... -}
Далее: когда вы используете библиотеки, такие как mtl или monadLib (например, типы, основанные на классе, вместо прямых трансформаторов), вы можете напрямую обращаться к большинству базовых монад:
c :: StateT MyState (ReaderT MyConfig SomeOtherMonad) Result
c = do
x <- ask
y <- get
{- ... -}
Наконец, если вам действительно нужно много подняться, несмотря на эти два момента, вам следует подумать о написании пользовательской монады или даже использовать совершенно другую абстракцию. Я обнаружил, что использую стрелку автомата для вычислений с учетом состояния вместо государственной монады.
Ответ 3
Я в основном уверен, что то, что вы описываете, невозможно для IO, которая всегда является самой внутренней монадой:
От Мартина Грабмюллера: Monad Transformers шаг за шагом, доступно на
http://www.grabmueller.de/martin/www/pub/
В этом документе мы вызываем liftIO в eval6 для выполнения операций ввода-вывода. Почему нам нужно поднимать в этом случае? Потому что нет класса IO для которые мы можем создать типа типа. Поэтому для операций ввода-вывода мы нужно вызвать лифт для отправки команд внутрь
В общем случае для монад, менее ограничивающих, чем IO, порядок (как, например, Error and State) по-прежнему имеет значение для семантики, поэтому вы не можете изменить порядок стека, чтобы упростить синтаксис.
Для некоторых монадов, таких как Reader, которые являются центральными (в том смысле, что они коммутируют в стеке), ваша идея кажется явно невозможной. Я не знаю, как это написать. Я предполагаю, что это будет класс типа CentralMonad
, который ReaderT
является экземпляром, с некоторой реализацией...