了解 Haskell 中的 RWST

Understanding the RWST in Haskell

提问人:Piskator 提问时间:10/9/2023 最后编辑:Piskator 更新时间:10/9/2023 访问量:82

问:

我研究了这个,试图理解几个变压器单子是如何相互作用的,特别是更好地理解单子并堆叠在一起。lift

对于这里找到的 RWST 单子(我认为这是最好的文档),它是一个堆叠的单子,其中 Reader、Writer、State 都是一个单子层(并且按照堆叠的顺序)。或者应该如何理解它? 当我查看定义时,我将其理解为将 reader-environment 视为状态环境,并将任何 monad 包装在 的返回值周围。这也意味着在这个单子中只存在两层单子。即,外部单子和包含多个单子的元组。runRWST :: r -> s -> m (a, s, w)mRWSm

这反过来也意味着您只能使用一次。将值从读取器状态单子提升到外部单子中。lift

从这个意义上说,它只是两个函数应用两个内部单子中的任何一个。对于最后一点,我仍然不确定我是否理解为什么即使阅读了这篇 stackoverflow 帖子,您也需要一个读者和一个状态单子。我猜读者只对只读有意义,但如果有人不想这样做,是否可以在两个分离状态单子周围使用变压器单子?getask

举个例子:

这些评论让我有理由思考并使以下内容更加明确......在以下类型定义中,什么是内部单子和外部单子?它本身是一个包裹着的单子(因此是一个外单子)(内单子)吗?RWSTEither String

type MyRWST a = RWST
                 (String -> Either String MyType)       
                 [Int] 
                 (MyEnv, [String], [String])
                 (Either String)
                 a
Haskell 函数编程 单子变压器 状态单子

评论

2赞 leftaroundabout 10/9/2023
等价于 、 和 的堆栈。无论你把它看作一个转换器,还是三个转换器,或者只看两个转换器,这是一个解释问题,但无论如何,类型系统把它看作是一个转换器,因此你只会使用一次 - 但这不会将值从读取器、状态或写入器提升到 RWST 单子中,而是从底层单子(例如)。- - 我正在努力理解你的问题到底是什么......ReaderTWriterTStateTliftIO
1赞 Joachim Breitner 10/9/2023
RWST 不是一个单子堆栈,它是一个实现多种功能(读取器、写入器、状态)的单个单子。
0赞 Piskator 10/9/2023
@leftaroundabout你说的“但来自底层单子”是什么意思,是 .arunRWST :: r -> s -> m (a, s, w)
0赞 Piskator 10/9/2023
那你@JoachimBreitner不同意 leftaroundabout 吗?
0赞 leftaroundabout 10/9/2023
@Piskator不,是m

答:

8赞 Silvio Mayolo 10/9/2023 #1

monad transformer 的内部 monad 始终是一个类型参数。您提供的类型不是转换器。

type MyRWST a = RWST
                 (String -> Either String MyType)       
                 [Int] 
                 (MyEnv, [String], [String])
                 (Either String)
                 a

这是一个 .将其与 MaybeT 等变压器进行比较。Monad

newtype MaybeT m a = MaybeT { runMaybeT :: m (Maybe a) }

MaybeT接受两个类型参数,第一个本身接受一个参数,所以它的种类是 。使用更明确的括号,即 .现在我们可以看到为什么它被称为变压器了。它接受一个单子(种类)并将其转换为一个新的单子(也是种类)。(* -> *) -> * -> *(* -> *) -> (* -> *)* -> ** -> *

RWST 定义为

newtype RWST r w s m a = RWST { unRWST :: r -> s -> w -> m (a, s, w) }

现在这需要很多类型参数,但是如果我们修复 、 和 ,我们就会得到一个转换器。也就是说,它本身不是一个单子变压器,但对于任何 , , 和 ,都是一个变压器。完整的种类是rwsRWSTrwsRWST r w sRWST

RWST :: * -> * -> * -> (* -> *) -> * -> *

虽然你可以认为它有三个层(读取器、写入器和状态),但它实际上只有一个层。的“下一层”真的是.所以直接回答你关于的问题,的类型签名是RWSTRWST r w s mmliftlift

lift :: (MonadTrans t, Monad m) => m a -> t m a

当 ,我们得到t ~ RWST r w s

lift :: Monad m => m a -> RWST r w s m a

因此,一个单曲将我们带到了整个混乱中。liftRWST r w s

评论

0赞 Piskator 10/9/2023
@leftaroundabout 和 Silvio Mayolo,是的,我最初所说的那个确实按照它的编写方式编译。
1赞 Silvio Mayolo 10/9/2023
@leftaroundabout 啊,谢谢!我把声明读作一个 not a ,所以我认为 OP 是他们自己定义的数据构造函数。我现在看到了这个问题。编辑了帖子。MyRWSTdatatypeRWST