7

我试图找出最简洁的方法来修改嵌套在Maybe类型(或其他用于建模偏爱的类型)中的值。

这是示例设置:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

data Outer = Outer { _inner :: Maybe Inner }
  deriving (Show)

data Inner = Inner { _foo :: Int }
  deriving (Show)

makeLenses ''Outer
makeLenses ''Inner

通过以下方式以有点混乱的方式很容易做到这一点lens

wibble :: Outer -> Maybe Outer
wibble o = do i <- view inner o
              let i' = over foo succ i
              return $ set inner (Just i') o

为了说明为什么这很讨厌,这就是我希望能够写的内容:

wibble' :: Outer -> Maybe Outer
wibble' = overish inner.foo succ

overish = ???

查找字段失败只会导致整个操作失败,而不是让我在可能发生的每个点显式检查失败。

有什么建议么?我试过浏览各种lens模块,但似乎没有什么能完全符合要求。

4

1 回答 1

8

您可以使用over (inner.traverse.foo)写入嵌套字段。但是,这不会以您想要的方式报告失败,因为它成功地映射了所有 0 个目标。

traverse(来自Data.Traversable,由 重新导出Control.Lens) 这里给你一个Traversal供你走过去的Maybe

您可以从中读取(^?)以查看镜头的目标是否存在。

我们可以通过使用现有的镜头组合器分别读写来解决这个问题,但我们可以直接构建这样的组合器:

import Data.Monoid (Any(..))
import Control.Monad (guard)

overish :: LensLike ((,) Any) s t a b -> (a -> b) -> s -> Maybe t
overish l f s = case l (\a -> (Any True, f a)) s of
  (Any r, t) -> t <$ guard r

你也可以这样写l %%~ \a -> (Any True, f a)

您可以使用 轻松检查 aTraversal是否没有目标nullOf,但这需要两次通过和更高等级的类型:

overish :: Traversal s t a b -> (a -> b) -> s -> Maybe t
overish l f s = over l f s <$ guard (not (nullOf l s))

这实际上只是检查是否有目标,然后如果有目标,则将 setter 应用于它。

然后你可以使用

overish (inner.traverse.foo) succ
于 2012-12-07T03:21:46.030 回答