我试图理解scalaz
中的Lens
es(令人惊讶的是,cats-core
没有找到类似的东西(,我遇到了所谓的Store
,这是一个类型别名:
type StoreT[F[_], A, B] = IndexedStoreT[F, A, A, B]
type IndexedStore[I, A, B] = IndexedStoreT[Id, I, A, B]
type Store[A, B] = StoreT[Id, A, B]
哪里
final case class IndexedStoreT[F[_], +I, A, B](run: (F[A => B], I))
问题是如何对待这种类型?文档仅引用Lens
es。有人可以用几句话解释一下吗?
对我来说,它看起来类似于State
monad,其中"状态转换"与函数F[A => B]
一起存储
Store[S,A]
是一个充满A
的结构,按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
将光标移动到其他像素。
商店科莫纳德
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
,则store.extend(mean)
将对整个图像执行高斯滤镜。新图像中的每个像素将是原始图像中该位置的像素周围的像素的平均值。
s.duplicate
为您提供了一个充满Store[S,A]
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
利用这种二元性的一种很酷的方法是,如果你有一个存储和一个状态机,它们就会相互消灭。存储驱动状态机,状态机反过来从存储中选取一个值。
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)
}