镜头中的视图和使用有啥区别?
Posted
技术标签:
【中文标题】镜头中的视图和使用有啥区别?【英文标题】:What's the difference between view and use in lens?镜头中的视图和使用有什么区别? 【发布时间】:2021-06-20 12:21:39 【问题描述】:有什么区别
view :: MonadReader s m => Getting a s a -> m a
和
use :: MonadState s m => Getting a s a -> m a
在Control.Lens.Getter?
【问题讨论】:
你了解MonadReader
和MonadState
的区别吗?
【参考方案1】:
lens getter 为我们提供了一个从源到目标的函数:
(^.) :: s -> Getting a s a -> a
flip (^.) :: Getting a s a -> s -> a
任何函数都可以做成MonadReader
计算,函数的参数类型为环境类型:
asks :: MonadReader s m => (s -> a) -> m a
既然如此,(^.)
可以推广到任何MonadReader
到asks
,从而产生view
:
view :: MonadReader s m => Getting a s a -> m a
view g = asks (\s -> s ^. g)
(我在这里使用的定义并不是您在the Control.Lens.Getter
source 中找到的定义,但就结果而言,它们等同于它们。)
以类似的方式,任何函数都可以变成MonadState
计算,保持状态不变,函数的参数类型作为状态类型:
gets :: MonadState s m => (s -> a) -> m a
据此,(^.)
也可以泛化为MonadState
到gets
之间的任意一个use
:
use :: MonadReader s m => Getting a s a -> m a
use g = gets (\s -> s ^. g)
从另一个角度来看,view
和 use
可以分别看作是 asks
和 gets
的变体,它们将 getter 作为参数,而不是直接作为函数。
关于view
最后一点,函数本身就是MonadReader
的实例。既然如此,view
可以用作(^.)
的前缀/非运算符版本。
【讨论】:
【参考方案2】:查看类型签名,view
采用 MonadReader
(例如 ReaderT
),use
采用 MonadState
(例如 StateT
)。现在,view
和 use
都有相同的目标:从我们正在查看的东西中提取合理的价值。
MonadReader
表示只读状态。我们可以使用ask
访问其中的值。 MonadState
代表读写状态,可以用get
检索。所以view
和use
都查询给定的monad 的内部状态,但是view
调用ask
而use
调用get
。一般来说,只有一种适用于您的情况。
查看这两个函数的源代码并不是特别有启发性,除非您已经了解镜头是如何实现的(如果您了解了,那么您可能了解view
和use
之间的区别),所以这是类型签名比代码本身更具启发性的一个很好的例子。
【讨论】:
“只有一个适用于你的情况”——我认为这忽略了一个关键点。如果只有一个适用,那么两者都没有意义。我们只会有更一般的view
,以及ask
,我们可以忘记use
/get
。 IMO,我们两者都有的原因是有可能有一个MonadReader s1
,它也是MonadState s2
(例如使用RWS
),现在我们确实需要两个操作来“读取只读状态”和“读取读写状态”,因此有两个与镜头相关的操作。
假设我不喜欢 RWS
的设计模式,或者在没有某种区分标识的情况下将两个状态组合在同一个 monad 堆栈中,那么 MonadState
是否有意义是MonadReader
的子类?
如果你不喜欢RWS
那么我想。但是我个人发现RWS
在很多情况下都非常有用,当我们有一些***配置环境(只读)和一些可变程序状态(读写)时,两者都没有办法共存,就像(正如您正确指出的那样)在使用 mtl 习语时,您不能轻易地将两个 MonadState
实例放在同一个堆栈中。
@AriFordsham 一个复杂的问题是MonadReader
也有local
作为一种方法,而local
在MonadState
中不合适。虽然原则上可以通过将MonadReader
分成两个类来避免这个问题,但 Silvio Mayolo 的反对意见仍然存在:能够在单个 monad 上分别处理固定环境和可变状态是有用的,并且子类关系将,就 mtl 而言,排除它。以上是关于镜头中的视图和使用有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章