简体   繁体   English

部分镜片的组成

[英]Composition of partial lenses

I'm trying to figure out the cleanest way to modify values nested inside of Maybe types (or other types for modeling partiality). 我试图找出最简洁的方法来修改嵌套在Maybe类型(或其他类型的建模偏好)内的值。

Here is the example setup: 以下是示例设置:

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

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

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

makeLenses ''Outer
makeLenses ''Inner

It's pretty easy to do this in a somewhat messy fashion with lens : 使用lens以一种有点混乱的方式很容易做到这一点:

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

To put a point on why this is nasty, here's what I would like to be able to write: 为了说明为什么这是令人讨厌的,这是我想要写的:

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

overish = ???

A failure to look up a field should just make the whole operation fail, rather than making me explicitly check for failure at each point where it might happen. 查找字段失败应该只是使整个操作失败,而不是让我在可能发生的每个点上明确地检查失败。

Any suggestions? 有什么建议么? I've tried navigating the various lens modules, but nothing has quite appeared to fit the bill. 我试过导航各种lens模块,但似乎没有任何东西适合这个法案。

You can use over (inner.traverse.foo) to write to the nested field. 您可以使用over (inner.traverse.foo)写入嵌套字段。 This will not report failure the way you want though, because it succeeded in mapping over all 0 targets. 这不会以您想要的方式报告失败,因为它成功映射了所有0个目标。

traverse (from Data.Traversable , re-exported by Control.Lens ) here gives you a Traversal for walking over the Maybe . traverse (来自Data.Traversable ,由Control.Lens重新导出),这里为您提供Traversal MaybeTraversal

You can read from it with (^?) to see if the target of the lens exists. 您可以使用(^?)读取它以查看镜头的目标是否存在。

We can solve this in several ways by using existing lens combinators to read and write separately, but we could just build such a combinator directly: 我们可以通过使用现有的镜头组合器分别进行读写来解决这个问题,但我们可以直接构建这样的组合器:

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

You could write this with l %%~ \\a -> (Any True, fa) as well. 你可以用l %%~ \\a -> (Any True, fa)来写这个。

You can easily check if a Traversal has no targets by using nullOf , but this will require two passes and a higher rank type: 您可以使用nullOf轻松检查Traversal是否没有目标,但这需要两次传递和更高级别的类型:

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

This is effectively just checking to see that there is a target and then applying the setter to it if there are targets. 这实际上只是检查是否有目标,然后在有目标的情况下将setter应用于它。

Then you can just use 然后你可以使用

overish (inner.traverse.foo) succ

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM