使用 Maybe 遍历嵌套记录的更短方法
Posted
技术标签:
【中文标题】使用 Maybe 遍历嵌套记录的更短方法【英文标题】:Shorter way to traverse nested record with Maybe 【发布时间】:2017-09-05 00:09:28 【问题描述】:是否有更短/更简洁的方式来编写以下 sn-p 代码:
fromMaybe "" $ fmap (^. fullName) (bi ^. bookerContact)
这里bi ^. bookerContact
可能会导致Maybe Contact
记录,这就是为什么需要对^. fullName
进行映射。在嵌套遍历之后,如果我们以 Nothing
结尾,我们使用 fromMaybe ""
将其默认为空字符串。
【问题讨论】:
【参考方案1】:一种简单的整理方法是使用maybe
而不是fromMaybe
/fmap
组合:
maybe "" (^. fullName) (bi ^. bookerContact)
您还可以引入_Just
棱镜将所有向下钻取表示为单个光学:
fromMaybe "" (bi ^? bookerContact . _Just . fullName)
请注意,我们已从 (^.)
切换到 (^?)
。这反映了将_Just
添加到链中如何将过去的镜头(并且恰好到达一个目标)变成了遍历(可能没有到达任何目标)。
也可以利用 Text
作为一个幺半群并使用来自 Data.Foldable
的 fold
...
fold (bi ^? bookerContact . _Just . fullName)
... 或 foldOf
,如果您更喜欢 lens 拼写:
foldOf (bookerContact . _Just . fullName) bi
正如the docs 指出的那样,foldOf
等同于(^.)
(which is essentially view
specialised to (->)
),所以这也可以通过折叠Maybe Text
:
bi ^. bookerContact . _Just . fullName
【讨论】:
嗯...我不清楚bi ^. bookerContact._Just.fullName
的类型是如何变成Text
的。不应该是Maybe Text
吗?
^. _Just
over a Maybe Text
会导致 ""
但是 ^. _Just
over a Maybe Int
会导致以下错误 - No instance for (Monoid Int) arising from a use of ‘_Just’
Is it possible to traverse over a @987654355 @ 以这样一种方式,即整个遍历导致 Maybe
不依赖于 Monoid
魔术?
@SaurabhNanda “神奇”在于 Maybe Text
被折叠(为了比较,["foo", "bar"] ^. traverse
导致 "foobar"
)。如果您更喜欢获得Maybe Text
而不是折叠它,请使用(^?)
而不是(^.)
,如答案中的第二个和第三个变体。 (根据您可以接受的魔法数量,在这种情况下,您可能会看到 foldOf
在显式性方面比 (^.)
有所改进。)
@PaulGardiner 我已经添加了文档的链接;谢谢你的建议。这在另一个方向上可能更容易理解:如果foo
是一个透镜,或者更一般地说是一个吸气剂,从s
到a
,(^.)
可以从@987654371 中提取a
@ 值,因此 \s -> s ^. foo
是一个 s -> a
函数。 view
做了几乎相同的事情,只是它将结果推广到任意的MonadReader
(即从s -> a
到MonadReader s m => m a
)。另见this other answer of mine,它涉及到这个问题。
^?
很干净。唯一不幸的是,lens
没有表达 仿射 遍历的方式,至少某些 profunctor 镜头公式会这样做。为了清楚起见,最好有一个特殊的运算符坚持其参数光学遍历最多一个元素。以上是关于使用 Maybe 遍历嵌套记录的更短方法的主要内容,如果未能解决你的问题,请参考以下文章