Как правильно конвертировать между списками и монадными преобразователями ListT? - PullRequest
6 голосов
/ 09 февраля 2012

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

В качестве простого примера: для преобразования из [a] в ListT a фактически требуется составление двухфункции:

conv :: (Monad m) => [a] -> ListT m a
conv = ListT . return

Хотя это просто, я удивлен, что его там еще нет.

Вопросы:

  • Есть ли что-то лучшеспособ справиться с недетерминизмом, где нужен монадный преобразователь?
  • Существуют ли какие-либо методы / библиотеки для чистого преобразования между списками и ListT?

1 Точные причины довольно сложнытак что я не хочу подробно останавливаться на этом.

Ответы [ 2 ]

6 голосов
/ 09 февраля 2012

Я не думаю, что для этого есть какие-либо библиотеки; В конце концов, conv - невероятно простая функция, а наоборот - просто runListT.

conv аналогично liftMaybe, который часто требуется при использовании MaybeT:

liftMaybe :: (Monad m) => Maybe a -> MaybeT m a
liftMaybe = MaybeT . return

Я бы порекомендовал назвать это что-то вроде liftList. 1

Что касается лучшего монадного преобразователя для недетерминизма, я рекомендую взглянуть на пакет logict , основанный на преобразователе LogicT Олега, который является монадой логики обратного отслеживания с продолжением с некоторые полезные операции . В качестве бонуса, поскольку [] является экземпляром MonadLogic, эти операции также работают со списками.


1 Интересно, что мы можем определить функцию, которая обобщает шаблон conv и liftMaybe:

import Data.Foldable (Foldable)
import qualified Data.Foldable as F

choose :: (Foldable t, MonadPlus m) => t a -> m a
choose = F.foldr (\a b -> return a `mplus` b) mzero

Это, вероятно, сделает ваш код довольно запутанным, поэтому я не рекомендую его использовать:)

0 голосов
/ 07 августа 2012

Я только что столкнулся с этим вопросом несколько месяцев спустя, потому что мне было интересно нечто похожее на это. Итак, я придумал следующее:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

import Control.Monad.Trans.Class
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.List


-- | Minimal implementation: either joinLift or joinT
class (MonadTrans t, Monad m) => MonadTransJoin t m | m -> t, t -> m where
    joinLift :: (Monad m', Monad (t m')) => m' (m a) -> t m' a
    joinLift = joinT . lift

    joinT :: (Monad m', Monad (t m')) => t m' (m a) -> t m' a
    joinT = (>>= (joinLift . return))


instance MonadTransJoin MaybeT Maybe where
    joinLift = MaybeT
    joinT = (>>= maybe mzero return)

instance MonadTransJoin ListT [] where
    joinLift = ListT
    joinT = (>>= foldr mcons mzero)
        where mcons x xs = return x `mplus` xs

Пока все хорошо - и мой joinT метод для пары ListT / [] выглядит так, как будто он как-то связан с choose.

от ehird.

Но проблема в том, что на самом деле не существует единого интерфейса между преобразователем монад и монадой, чье поведение она наделяет своей базовой монадой. У нас есть MaybeT :: m (Maybe a) -> MaybeT m a и ListT :: m [a] -> ListT m a, но OTOH у нас StateT :: (s -> m (a, s)) -> StateT s m a. Я не знаю, есть ли способ обойти это - определенно требуется

...