I'm trying to understand Lens
es in scalaz
(surprisingly didn't find something similar in cats-core
) and I came across the so called Store
which is a type alias:
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]
Where
final case class IndexedStoreT[F[_], +I, A, B](run: (F[A => B], I))
The question is how to treat this type? The documentation just referes to Lens
es. Can someone give an explanation in a few words?
To me it looks similar to State
monad where the "state transition" is storing with function F[A => B]
A
Store[S,A]
is a structure full ofA
s, indexed byS
, with a distinguishedS
as a kind of "cursor" into the structure.To answer the question "what is it?", it's most instructive to look at what operations it supports.
You can ask for the position of the cursor:
You can "peek" at the value under the cursor:
And you can "seek" to move the cursor:
Think of it as an array of dimensions
S
, where you have an indexs:S
into the array, and you can move the index.For example,
Store[(Int,Int), Byte]
is a two-dimensional 256-colour bitmap. You canpeek
at the colour (represented by a byte) of the pixel under the cursor, and you can move the cursor to a different pixel usingseek
.The store comonad
Store[S,_]
is also a comonad. This means it supports the following operations:map
means you can change all the values in the store using a function.s.extend(f)
takes a "local" computationf
, that operates on the store at a particular location ofS
, and extends it to a "global" computation that operates on the store at every location. In the bitmap example, if you have a functionmean(store)
that takes the average of the pixels immediately surrounding the cursor in thestore
, thenstore.extend(mean)
will perform a Gaussian filter on the whole image. Every pixel in the new image will be an average of the pixels immediately surrounding the pixels at that location in the original image.s.duplicate
gives you aStore[S,Store[S,A]]
full ofStore[S,A]
s, that at every locationS
has a copy of the originalStore[S,A]
with its cursor set to that locationS
.Relation to State
Store
is the dual ofState
. Under the hood,State[S,A]
is reallyS => (A, S)
, andStore[S,A]
is really(S => A, S)
:Both are composed of the two functors
S => _
and(_, S)
. If you compose them one way, you getState[S,_]
. If you compose them the other way, you getStore[S,_]
.I gave a talk about this a couple of times:
https://www.youtube.com/watch?v=FuOZjYVb7g0
A cool way you can exploit this duality is that if you have a store and a state machine, they annihilate each other. The store drives the state machine, and in turn the state machine picks a value from the store.