5

我有一张从 A 型到 B 型的全图。

import qualified Data.Map as M
import Data.Maybe

tmGet k m = fromJust $ M.lookup k m

tmSet k v = M.insert k v

我用作Data.Map示例实现,但它可以是任何东西,例如一个Array甚至是一个Bool索引元组:

tmGet True  = fst
tmGet False = snd

我想要一个tmAt构建镜头的功能:

(42, 665) ^. tmAt True == 42
(42, 665) & tmAt False +~ 1 == (42, 666)

问题是我如何构造tmAttmGettmSettmModify)?

4

2 回答 2

3

如果您查看Control.Lens 模块的文档,就会看到镜头包不同部分的非常方便的图像。既然要构造 a Lens,可以看图的Lens部分。显示的最顶层函数是

lens :: (s -> a) -> (s -> b -> t) -> Lens s t a b

这会从 getter 和 setter 构造一个 Lens。

该函数s -> a是一个 getter——类型签名的意思是,“如果你给我一个数据结构s,我会从中挑选一个a值。” s -> b -> t函数是setter,类型签名的意思是,“如果你给我一个和s一个新的值b,我会为你创建一个新的结构t。” (类型不同,因为镜头实际上可以改变事物的类型。)

如果你的 getter 是tmGet,而你的 setter 是tmSet,那么你可以用

tmAt :: Boolean -> Lens s t a b
tmAt b = lens (tmGet b) (tmSet b)

无论您的实际stab参数是什么。在元组的示例中,它将是

tmAt :: Bool -> Lens (a, a) (a, a) a a

(换句话说,如果你给 Lens 一个 function a -> a,它可以将一个(a, a)-tuple 转换为另一个(a, a)-tuple。)


如果你想花哨,你也可以重写tmAt

tmAt = lens <$> tmGet <*> tmSet
于 2013-10-15T13:16:08.987 回答
0

一旦你定义tmGettmSet成为类型

tmGet :: k -> f v -> v
tmSet :: k -> f v -> v -> f v

lens您可以通过向镜头构造函数提供参数化的 getter 和 setter来定义一系列镜头。以下是对由 a 索引的一对执行此操作的方法Bool

tmGet True  = fst
tmGet False = snd

tmSet True  (_,b) a = (a,b)
tmSet False (a,_) b = (a,b)

tmAt b = lens (tmGet b) (tmSet b)

现在在 GHCI

>>> (42,665) ^. tmAt True
42
>>> (42,665) & tmAt False +~ 1
(42,666)
于 2013-10-15T13:12:28.253 回答