View Source Lens2.Makers (Lens 2 v0.2.1)
Two ways of defining named lens-making functions that improve on def
.
Lens makers are named functions that, when called, return a lens. Lens makers are created in three ways:
Using plain old
def
:def leaf(key), do: Lens.key(:down) |> Lens.key(key)
This is fine, but you cannot use
leaf
itself in a pipeline. That is, this will fail to compile:Lens.key(:upper) |> leaf(:bottom) error: undefined function leaf/2
Because of that, it's better to use
defmaker/2
(alternately,deflens/2
). This creates a lens maker from one or more other lens makers, most often by piping them into each other. For example, a lens maker for two-level maps could be defined like this:defmaker leaf(key), do: Lens.key(:down) |> Lens.key(key) # Works fine: Lens.key(:upper) |> leaf(:bottom)
def_raw_maker/2
(alternately,deflens_raw/2
, is used for cases where a lens maker can't be made from other lens makers. For example, the definition ofLens2.Lenses.Keyed.key!/1
looks like this:@spec key!(any) :: Lens2.lens def_raw_maker key!(key) do fn composed, descender -> {gotten, updated} = descender.(DefOps.fetch!(composed, key)) {[gotten], DefOps.put(composed, key, updated)} end end
Summary
Functions
Import the maker macros.
Write a lens maker without using other lens makers.
Alternate spelling of defmaker/2
.
Alternate spelling of def_raw_maker/2
.
Like deflens
, but creates private functions.
Create a lens maker from one or more other lens makers.
Like defmaker
, but creates private functions.
Functions
Import the maker macros.
Write a lens maker without using other lens makers.
The body of the function must return a two-argument function that takes a container and a "descender" function:
@spec key!(any) :: Lens2.lens
def_raw_maker key!(key) do
fn container, descender ->
...
end
end
The job of the returned function is to take the container, take a value within it, and pass it to the descender:
def_raw_maker key(key) do
fn container, descender ->
... = descender.(DefOps.fetch!(container, key))
...
end
end
(Defops.fetch!
calls Map.fetch!/2
in the case of a map or
Access.fetch!/2
otherwise. I presume the map version of fetch!
is more efficient than the Access
version.)
The result is a tuple:
If the original container was being operated on by
Lens2.Deeply.get_and_update/3
orget_and_update_in/3
, the first element contains the "get" and the second is the "update".If the operation was a put or an update, the same is done – I guess you've got the value right there, so you might as well pass it along. The original operation will discards it.
If the operation is a get, the gotten value will be both values of the tuple.
Your code doesn't have to worry about that: it can simply assume
get_and_update
is being used.
fn container, descender ->
{gotten, updated} = descender.(...)
...
end
end
Your code has to bundle up the gotten
and updated
functions into
a tuple of its own and return that:
fn container, descender ->
{gotten, updated} = descender.(DefOps.fetch!(container, key))
{[gotten], DefOps.put(container, key, updated)}
end
The "updated" case involves putting the new value in place of the one that was extracted "on the way down". The "gotten" case is more subtle and I haven't figured out how to explain it. This paragraph should link to a tutorial I haven't written yet.
Alternate spelling of defmaker/2
.
This is the equivalent macro from Lens 1. You won't break my heart
if you prefer it to defmaker/1
, but I had early troubles confusing
a lens and a lens maker. I suspect this name will help other novices avoid my
mistakes.
I suppose I should deprecate the Lens
alias (Lens.key?(:a)
, etc.), but:
Do I contradict myself?
Very well then I contradict myself,
(I am large, I contain multitudes.)– Walt Whitman, Leaves of Grass
Alternate spelling of def_raw_maker/2
.
This is the equivalent macro from Lens 1.
Like deflens
, but creates private functions.
Create a lens maker from one or more other lens makers.
Most often, this is used to name a pipeline of lenses (and make the resulting function suitable for including in other pipelines):
defmaker leaf(key), do: Lens.key(:down) |> Lens.key(key)
The body after the do:
can be arbitrary code:
defmaker keys(keys) do
keys |> Enum.map(&key/1) |> Combine.multiple
end
The only requirement is that the last thing the code does must be to
call a lens maker. (Or, I suppose, produce a lens in some other
way, but you'd probably use def_raw_maker/2
for that.)
Like defmaker
, but creates private functions.