如何在列表和 ListT monad 转换器之间干净地转换?

发布于 2025-01-04 05:30:01 字数 491 浏览 1 评论 0原文

我目前正在编写一个项目,其中大量使用 ListT monad 转换器。使用普通列表时,实现非确定性非常容易。然而,一旦我必须将代码转换为 ListT,它就会变得更加复杂1

举一个简单的例子:从 [a] 转换为 ListT a 实际上需要编写两个函数:

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

虽然很简单,但我很惊讶它还没有实现。

问题

  • 是否有更好的方法来处理需要 monad 转换器的不确定性?
  • 是否有任何技术/库可以在列表和 ListT 之间干净地来回转换?

1 具体原因很复杂,我不想过多阐述。

I am currently writing a project where I make a heavy use of ListT monad transformer. When using plain lists, implementing nondeterminism is very easy. However once I had to convert my code to ListT, it got much more complicated 1.

As a simple example: converting from [a] to ListT a actually requires composing two functions:

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

Though it's simple, I am surprised it's not already there.

Questions:

  • Is there some better way to handle nondeterminism where a monad transformer is needed?
  • Are there any techniques / libraries for converting cleanly back and forth between lists and ListT?

1 The exact reasons are quite complicated, so I don't really want to elaborate too much on that.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

终陌 2025-01-11 05:30:01

我不认为有任何图书馆可以做到这一点;毕竟,conv 是一个极其简单的函数,而反过来就是 runListT

conv 类似于使用 MaybeT 时经常需要的 liftMaybe

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

我建议将其命名为 liftList >.1

至于用于非确定性的更好的 monad 转换器,我建议查看 逻辑包,基于 Oleg 的 LogicT 转换器,它是一个基于连续的回溯逻辑 monad,具有 一些有用的操作。作为奖励,由于 []MonadLogic 的实例,因此这些操作也适用于列表。


1 有趣的是,我们可以定义一个函数来概括 convliftMaybe 的模式:

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

这可能会让您的代码非常混乱,所以我不这样做不建议使用它:)

I don't think there are any libraries for this; conv is an incredibly simple function, after all, and the other way around is just runListT.

conv is similar to the liftMaybe often desired when using MaybeT:

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

I would recommend naming it something along the lines of liftList.1

As far as a better monad transformer for nondeterminism goes, I recommend taking a look at the logict package, based on Oleg's LogicT transformer, which is a continuation-based backtracking logic monad with some helpful operations. As a bonus, since [] is an instance of MonadLogic, those operations also work on lists.


1 Interestingly, we can define a function that generalises the pattern of conv and 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

This will probably make your code quite confusing, so I don't recommend using it :)

折戟 2025-01-11 05:30:01

几个月后我才遇到这个问题,因为我想知道类似的事情。所以我想出了以下方法:

{-# 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

到目前为止一切顺利 - 我的 ListT/[] 对的 joinT 方法看起来有一些东西与 ehird 的选择有关。

但这样做的问题是,单子转换器和单子之间实际上没有统一的接口,单子的行为被赋予了基本单子。我们有 MaybeT :: m (Maybe a) -> MaybeT m a 和 ListT :: m [a] -> ListT m a,但是我们有 StateT :: (s -> m (a, s)) -> StateT sm a。我不知道是否有办法解决这个问题——它肯定需要

I just came across this question a few months later because I was wondering something similar to this. So I came up with the following:

{-# 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

So far so good—and my joinT method for the ListT/[] pair looks like it has something to do with ehird's choose.

But the problem with this is that there is actually no uniform interface between a monad transformer and the monad whose behavior it endows to its base monad. We have MaybeT :: m (Maybe a) -> MaybeT m a and ListT :: m [a] -> ListT m a, but OTOH we have StateT :: (s -> m (a, s)) -> StateT s m a. I don't know if there's a way to get around this—it certaindly requires

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文