使用 Haskell 的镜头库 fmap 一个镜头
Use Haskell's lenses library to fmap a lens
在下面的代码中,我的问题涉及最顶层的函数someFunc
(下面的一切只是为了提供一个完整的例子)。我在那里使用记录语法 getter 和 fmap
。 someFunc
的镜头实现方式是什么?
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用在左手的结果上。
在下面的代码中,我的问题涉及最顶层的函数someFunc
(下面的一切只是为了提供一个完整的例子)。我在那里使用记录语法 getter 和 fmap
。 someFunc
的镜头实现方式是什么?
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用在左手的结果上。