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:

  1. 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
  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)
  3. 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 of Lens2.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.

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.

Link to this macro

def_raw_maker(header, list)

View Source (macro)

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 or get_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.

Link to this macro

deflens(header, list)

View Source (macro)

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

Link to this macro

deflens_raw(header, list)

View Source (macro)

Alternate spelling of def_raw_maker/2.

This is the equivalent macro from Lens 1.

Link to this macro

deflensp(header, list)

View Source (macro)

Like deflens, but creates private functions.

Link to this macro

defmaker(header, list)

View Source (macro)

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.)

Link to this macro

defmakerp(header, list)

View Source (macro)

Like defmaker, but creates private functions.