使用 Haskell 的镜头库 fmap 一个镜头

Use Haskell's lenses library to fmap a lens

在下面的代码中,我的问题涉及最顶层的函数someFunc(下面的一切只是为了提供一个完整的例子)。我在那里使用记录语法 getter 和 fmapsomeFunc 的镜头实现方式是什么?

import           Control.Lens
import           Data.IntMap  (IntMap)

someFunc :: Farm -> IntMap Size
someFunc farm =
  _barnSize <$> farm ^. farmBarns

data Farm = Farm
  { _farmBarns :: IntMap Barn
  }

farmBarns :: Lens' Farm (IntMap Barn)
farmBarns = lens _farmBarns (\farm barns -> farm { _farmBarns = barns } )

type Size = (Int, Int)

data Barn = Barn
  { _barnSize :: Size
  }

barnSize :: Lens' Barn Size
barnSize = lens _barnSize (\barn size -> barn { _barnSize = size } )

只需将 _barnSize 替换为 (^. barnSize) 或等效地 view barnSize:

someFunc :: Farm -> IntMap Size
someFunc farm = view barnSize <$> farm ^. farmBarns

对于“100% lens”解决方案,您可以使用 mapped setter。不过,在这种情况下,我认为这样做没有任何真正的优势。

someFunc :: Farm -> IntMap Size
someFunc farm = (mapped %~ view barnSize) (farm ^. farmBarns)

另一种可能的拼写涉及使用 to 将所有内容组合成一个 getter。这在这里也不会给你带来太多好处,但如果你想通过链接额外的 getters/folds/etc 继续使用 lens 样式中的 IntMap .

someFunc :: Farm -> IntMap Size
someFunc farm = farm ^. farmBarns . to (fmap (view barnSize))

有一个特殊用途的组合器包含上面的 to/(^.) 组合。它被称为 views:

someFunc :: Farm -> IntMap Size
someFunc farm = views farmBarns (fmap (view barnSize)) farm

您可以使用 mapped(或 traversed)到 'map' 使用镜头遍历,在您的情况下:

someFunc :: Farm -> IntMap Size
someFunc farm =
    farm ^. farmBarns & mapped %~ view barnSize

这可能有点令人困惑,但这是正在发生的事情,我将添加括号以使其更清楚一些

someFunc :: Farm -> IntMap Size
someFunc farm =
    (farm ^. farmBarns) & (mapped %~ (view barnSize))

所以基本上,我们使用 farm ^. farmBarns 从农场获取 IntMap Barns,在 & 的右侧,我们使用 [=17 构造一个 setter =] 是 over 的中缀,然而 over 将其目标传递给的函数实际上只是使用镜头聚焦 barnSize。 view barnSize :: Barn -> Size

最后我们用&把它们连在一起,相当于flip $,把右手的setter用在左手的结果上。