镜头修改失败

时间:2019-07-05 21:38:44

标签: haskell lens

Control.Lens.Lens中,有一个功能

modifying :: MonadState s m => ASetter s s a b -> (a -> b) -> m ()

允许通过纯函数MonadState转换(a -> b)状态下的镜头下的值。

但是,我们可能希望让转换函数在m中失败,要求它的类型为(a -> m b)

我已经在镜头库中查找了这种功能,但是找不到,所以我实现了:

modifyingM l f = use l >>= f >>= assign l

哪个可以解决问题,但我想知道镜头库中是否已经有一个函数可以做到这一点。

1 个答案:

答案 0 :(得分:6)

我什么都没看到。 ASetter已定义

type ASetter s t a b = (a -> Identity b) -> s -> Identity t

因此它的功能还不够强大(Setter也无法做到)。另一方面,事实证明Lens比必需的要强一些。那么,让我们考虑如何使用Traversal

type Traversal s t a b =
  forall f. Applicative f => (a -> f b) -> s -> f t

所以

Traversal s s a b =
  forall f. Applicative f => (a -> f b) -> s -> f s

我们想要哪个Applicativem似乎是一个显而易见的尝试。当我们通过遍历a -> m b时,我们将返回s -> m s。大!与lens一样,我们实际上只要求用户提供一个ATraversal,我们可以对其进行克隆。

modifyingM
  :: MonadState s m
  => ATraversal s s a b
  -> (a -> m b) -> m ()
modifyingM t f = do
  s <- get
  s' <- cloneTraversal t f s
  put s'

这很好,因为它只遍历一次状态。

即使那是矫kill过正。实际上,最自然的事情是

modifyingM
  :: MonadState s m
  => LensLike m s s a b
  -> (a -> m b) -> m ()
modifyingM t f = do
  s <- get
  s' <- t f s
  put s'

您可以将其直接应用于TraversalLensIsoEquality,或使用cloneTraversalcloneLens,{ {1}},或者(希望在cloneIso的下一版本中)lens将其应用于单态变体。