关于选择在何处应用 monad 转换器的 monad 参数

问题描述 投票:0回答:1

采用

MaybeT
monad 转换器:

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

我没想到它会有不同的定义,因为

Maybe
只是一种带有(可选)内容的盒子(上面的类型
a
),所以
MaybeT
可以用参数做什么
m
,如果不把整个
Maybe
包裹在里面呢?写
:: Maybe (m a)
是没有意义的,因为这只是
someaction <$> theMaybe
的类型。

但在其他情况下,可能还有其他选择。

采用

StateT
monad 转换器,定义为

newtype StateT s m a = StateT { runStateT :: s -> m (a, s) }

这个定义,如果我根据

State
monad 的定义来看待它(显然,如果它没有通过
StateT
Identity
之上定义),

newtype State s a = State { runState :: s -> (a, s) }

似乎符合“一个函数

a -> b
作为一个包含内容
b
的容器”
的解释,这就是我最初理解
Functor
(->) r
实例的含义是什么。公平地说,
m
正在包装函数的“内容”(即状态计算的“内容”)。

但这(或者在我看来!)与

MaybeT
的情况完全不同,其中
m
并不包裹
Maybe
的内部,即
a
,而是整个
Maybe a

如果

StateT
必须类似于
MaybeT
(实际上使用
m
来包裹整个东西),它就会像这样

newtype StateT' s m a = StateT' { runStateT' :: m (s -> (a,s)) }

想到这里,我想到了一些想法:

  • 这样做会有什么问题吗?
  • 如果没有什么明显的错误,这样的 monad 转换器会有什么帮助吗?
  • 我认为它的错误之处在于,它感觉太不受限制了,在某种意义上,在
    IO
    之上运行这样的 monad 转换器意味着人们可以从
    s -> (a, s)
    monad,而如果实际的
    IO
    堆叠在
    StateT
    之上,则
    IO
    只能用于获取新的
    IO
    tate 和结果
    s
    ;但我仍然不确定我是否完全理解其中的区别。
    
    
  • 最后,这个怎么样?

a

这感觉有点没用,因为...感觉状态计算没有意义,因为,再次以 
newtype StateT'' s m a = StateT'' { runStateT'' :: s -> (m a,s) }

m
为例,
IO
将来自
a
,但是...哦,我真的不明白。
重点是,我体验过 

IO

的实用性,但我并不真正理解为什么

 有用,而其他替代方案则不然。或者他们会吗?
    

haskell functional-programming monad-transformers state-monad io-monad
1个回答
1
投票
要考虑什么可能会出错,请考虑

StateT

 并思考哪些效果可以由您的类型表示,哪些效果不能。

m = IO
嗯,这变成了 

newtype StateT' s m a = StateT' { runStateT' :: m (s -> (a,s)) }

,因此它会在读取当前 
IO (s -> (a,s))状态以生成新的 s
状态(以及结果 
s
)之前执行 IO 效果。
所以,这不能表达“读取当前状态并打印它”的计算。没那么有用。

a

我们得到
newtype StateT'' s m a = StateT'' { runStateT'' :: s -> (m a,s) }
。从中很容易获得一个以纯粹方式计算新状态的函数

s -> (IO a,s)

。这意味着IO不能影响新的状态。
因此,这不能表达“要求用户进行键盘输入,并据此更改状态”的计算。也没那么有用。

© www.soinside.com 2019 - 2024. All rights reserved.