lenses for elixir
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Lenses in Elixir


import Elins
# make a kitten to alter
kitten = %{name: "Mr. Bigglesworth", color: %{r: 1.0, g: 1.0, b: 1.0}}

# make a new kitten with a (deep) property set to a new value
kitten |> set([:color, :b], 0.0).()
# result: new kitten with 0 blue

# make a new kitten with a (deep) property changed
kitten |> edit([:color, :g], fn(v) -> 0.5 * v end).()
# result: new kitten with green halved

# set multiple properties (deep merge)
kitten |> setVals(%{ name: "Garfield", color: %{ r: 0.5 } }).()
# result: new kitten with name and red color changed

# make a new kitten with multiple properties changed by function
kitten |> editVals(%{ name: &String.upcase/1, color: %{ r: &(0.5 * &1) } }).()
# result: new kitten with name upcase'd and red halved

# make a new kitten with multiple properties changed through maps and lists
%{name: "Mr. Bigglesworth", children: [ %{num: 1}, %{num: 2}, %{num: 3} ] } |>
editVals(%{ name: &String.upcase/1, children: [ %{ num: &(&1 + 1) } ] }).()
# result: new kitten with name upcase'd and each child number incremented

# properties can even derive from other properties
total = fn(_x, cat) ->
  clr = cat.color
  clr.r + clr.g + clr.b
kitten |> editVals(%{
  age: fn(_x, cat) -> cat.name |> String.length() end,
  color: %{ r: total, g: total, b: total }
# result: %{name: "Mr. Bigglesworth", age: 16, color: %{r: 3.0, g: 5.0, b: 9.0}}
# note that since color properties on kitten were defined in order r -> g -> b,
# the alterations are triggered in the same order. hence blue gets more here.

Beside Maps these work on Structs as well (which essentially are Maps anyway).

This started as a minimal Elixir port of @jlouis's erl-lenses, which was to be superseded by his forge, but expanded from there to expose this friendlier interface.

  • Q: how about chaining multiple functions?

  • A: compose them.

  • Q: Why the ugly .()? :(

  • A: It ends up nicer for iteration; you could do say my_kittens |> Enum.map(setVals(%{ ... })) to have the lens initialize once then get used for each item in your list.