Работа над перестановочным стеком трансформатора монады

Одна из проблем с монадными трансформаторами, которые я нахожу, - это необходимость 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 является экземпляром, с некоторой реализацией...