如何构成"Maybe"个镜头?

How to compose "Maybe" lenses?

如果我有用于嵌套记录的镜头,其中每个镜头 returns 一个 Maybe,我怎样才能让它们合成,以便 "traversal" returns一个Nothing最后的结果是一个Nothing?

data Client = Client
  {
    clientProperties :: Maybe Properties
  , ...
  }

data Properties = Properties
  {
    propSmtpConfig :: Maybe SmtpConfig
  , ...
  }

c :: Client 
c = undefined

smtp = c ^. (properties . smtpConfig) -- How to make these lenses compose?

编辑 我尝试了很多选项,但这是我能想到的最好的。寻找更清洁的东西:

(client ^. properties) >>= (view smtpConfig)

您可以使用_Just prism。这是一个人为的例子:

> (Just (Just 1, ()), ()) & _1 . _Just . _1 . _Just +~ 1
(Just (Just 2,()),())

你的情况,我想你想要

properties . _Just . smtpConfig . _Just

traverse是有效的Traversal,记住。

getSmtpConfig :: Traversal' Client SmtpConfig
getSmtpConfig = properties . traverse . smtpConfig . traverse

Traversal 是你在这里能做的最好的 - 你不能得到 Lens - 因为可能没有 SmtpConfig。 (Lens 表示 "there's always exactly one of these things",而 Traversal 表示 "there may be zero or many"。)

这段代码实际上产生了与您使用 相同的 Traversal,但如果您还没有 grokked 棱镜,它可能更容易理解。

请注意,由于 Traversal 可能找不到任何结果,因此您不能使用 ^. to access a single result as you did in your question. You need to use the "safe head" operator ^? (aka flip preview)。

smtp :: Maybe SmtpConfig
smtp = c^?properties.traverse.smtpConfig.traverse
client ^? properties . _Just . smtpConfig . _Just

编辑:像透镜这样的光学器件可以将小动作变成大动作。光学构图是函数构图。 _Justa 上的操作转换为 Maybe a 上的操作。镜头可以将阅读的小动作变成阅读的大动作,将书写的小动作变成书写的大动作,但是_Just无法处理阅读动作,因为Nothing中没有a。因此_Just是一种比透镜弱的光学,一种穿越。复合光学器件始终可以准确地处理所有部件都可以处理的那些动作。所以properties . _Just . smtpConfig . _Just是一次遍历。 (^?) 使用遍历可以处理的读取操作的变体:“也许读取一个值”。因此,上面的行将平凡成功的阅读操作 Just :: SmtpConfig -> Maybe SmtpConfig 变成了一个大的阅读操作 Client -> Maybe SmtpConfig.