A Store[S,A]
是一个充满了A
s,索引为S
,具有杰出的S
作为结构中的一种“光标”。
要回答“它是什么?”的问题,最有启发性的做法是查看它支持哪些操作。
您可以询问光标的位置:
_.pos : Store[S,A] => S
您可以“查看”光标下的值:
_.peek : Store[S,A] => A
并且您可以“寻求”移动光标:
_ seek _ : (Store[S,A], S) => Store[S,A]
将其视为维度数组S
,你有一个索引s:S
到数组中,你可以移动索引。
例如,Store[(Int,Int), Byte]
是二维 256 色位图。你可以peek
在光标下像素的颜色(由字节表示)处,您可以使用以下命令将光标移动到不同的像素seek
.
商店comonad
Store[S,_]
也是一个共同点。这意味着它支持以下操作:
map : (A => B) => (Store[S,A] => Store[S,B])
extend : (Store[S,A] => B) => (Store[S,A] => Store[S,B])
duplicate : Store[S,A] => Store[S, Store[S, A]]
map
意味着您可以使用函数更改存储中的所有值。
s.extend(f)
进行“本地”计算f
,在特定位置的商店中运营S
,并将其扩展到在每个位置的商店上运行的“全局”计算。在位图示例中,如果您有一个函数mean(store)
取光标周围像素的平均值store
, then store.extend(mean)
将对整个图像执行高斯滤波器。新图像中的每个像素将是原始图像中该位置像素周围紧邻像素的平均值。
s.duplicate
给你一个Store[S,Store[S,A]]
充满Store[S,A]
s,在每个位置S
有原件的副本Store[S,A]
将光标设置到该位置S
.
与国家的关系
Store
is the dual of State
。在引擎盖下,State[S,A]
是真的S => (A, S)
, and Store[S,A]
是真的(S => A, S)
:
State[S,A] ~= S => (A, S)
Store[S,A] ~= (S => A, S)
两者都由两个函子组成S => _
and (_, S)
。如果你以一种方式编写它们,你会得到State[S,_]
。如果你以另一种方式组合它们,你会得到Store[S,_]
.
我曾多次谈论过这个问题:
https://www.youtube.com/watch?v=FuOZjYVb7g0 https://www.youtube.com/watch?v=FuOZjYVb7g0
利用这种二元性的一个很酷的方法是,如果你有一个商店和一个状态机,它们就会互相消灭。存储驱动状态机,状态机又从存储中选取一个值。
def zap[S,A,B](state: State[S,A], store: Store[S,B]): (A,B) = {
val (a, s) = state.run(store.pos)
(a, store.seek(s).peek)
}