Store[S,A] 是一个充满As 的结构,以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 色位图。你可以peekpeek在光标下像素的颜色(用一个字节表示),你可以使用seek将光标移动到不同的像素。
商店comonad
Store[S,_] 也是一个comonad。这意味着它支持以下操作:
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 中光标周围的像素取平均值,那么store.extend(mean) 将对整个图像执行高斯滤波器。新图像中的每个像素都是原始图像中该位置周围像素的平均值。
s.duplicate 为您提供一个充满Store[S,A]s 的Store[S,Store[S,A]],它在每个位置S 都有一个原始Store[S,A] 的副本,其光标设置为该位置S。
与国家的关系
Store 是State 的对偶。在后台,State[S,A] 真的是 S => (A, S),Store[S,A] 真的是 (S => A, S):
State[S,A] ~= S => (A, S)
Store[S,A] ~= (S => A, S)
两者都由S => _ 和(_, S) 两个函子组成。如果你以一种方式组合它们,你会得到State[S,_]。如果你以另一种方式编写它们,你会得到Store[S,_]。
我曾多次讨论过这个问题:
https://www.youtube.com/watch?v=FuOZjYVb7g0
利用这种二元性的一个很酷的方法是,如果你有一个 store 和一个状态机,它们就会互相消灭。存储驱动状态机,然后状态机从存储中选择一个值。
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)
}