focus v0.1.1 Focus.Lens

Experimenting with functional lenses.

Summary

Functions

Compose a pair of lenses to operate at the same level as one another. Calling Lens.view/2, Lens.over/3, or Lens.set/3 on an alongside composed pair returns a two-element tuple of the result

Given a list of lenses and a structure, apply Lens.view for each lens to the structure

Compose with most general lens on the left

Partially apply a lens to Lens.over/3, returning a function that takes a Focus.traversable and an update function

Partially apply a lens to Lens.set/3, returning a function that takes a Focus.traversable and a new value

Fix Lens.view!/2 on a given lens. This partially applies Lens.view/2 with the given lens and returns a function that takes a Focus.traversable structure

Define a lens to focus on a part of a data structure

Modify the part of a data structure that a lens focuses on

Update the part of a data structure the lens focuses on

Get a piece of a data structure that a lens focuses on; returns {:ok, data} | {:error, :bad_lens_path}

Get a piece of a data structure that a lens focuses on

Infix lens composition

Types

t()
t() :: %Focus.Lens{get: (any -> any), put: ((any -> any) -> any)}

Functions

alongside(lens1, lens2)

Compose a pair of lenses to operate at the same level as one another. Calling Lens.view/2, Lens.over/3, or Lens.set/3 on an alongside composed pair returns a two-element tuple of the result.

Examples

iex> alias Focus.Lens
iex> nums = [1,2,3,4,5,6]
iex> Lens.alongside(Lens.make_lens(0), Lens.make_lens(3))
...> |> Lens.view!(nums)
{1, 4}

iex> alias Focus.Lens
iex> bart = %{name: "Bart", parents: {"Homer", "Marge"}, age: 10}
iex> Lens.alongside(Lens.make_lens(:name), Lens.make_lens(:age))
...> |> Lens.view!(bart)
{"Bart", 10}
apply_list(lenses, structure)
apply_list([Focus.Lens.t], Focus.traversable) :: [any]

Given a list of lenses and a structure, apply Lens.view for each lens to the structure.

Examples

iex> homer = %{
...>   name: "Homer",
...>   job: "Nuclear Safety Inspector",
...>   children: ["Bart", "Lisa", "Maggie"]
...> }
iex> lenses = [Focus.Lens.make_lens(:name), Focus.Lens.make_lens(:children)]
iex> Focus.Lens.apply_list(lenses, homer)
["Homer", ["Bart", "Lisa", "Maggie"]]
compose(lens1, lens2)

Compose with most general lens on the left

Examples

iex> alias Focus.Lens
iex> marge = %{name: "Marge", address: %{street: "123 Fake St.", city: "Springfield"}}
iex> address_lens = Lens.make_lens(:address)
iex> street_lens = Lens.make_lens(:street)
iex> composed = Lens.compose(address_lens, street_lens)
iex> Lens.view(composed, marge)
{:ok, "123 Fake St."}
fix_over(lens, f \\ fn x -> x end)
fix_over(Focus.Lens.t, (any -> any)) :: (Focus.traversable -> Focus.traversable)

Partially apply a lens to Lens.over/3, returning a function that takes a Focus.traversable and an update function.

Examples

iex> alias Focus.Lens iex> upcase_name = Lens.make_lens(:name) …> |> Lens.fix_over(&String.upcase/1) iex> %{name: “Bart”, parents: {“Homer”, “Marge”}} …> |> upcase_name.() %{name: “BART”, parents: {“Homer”, “Marge”}}

fix_set(lens)

Partially apply a lens to Lens.set/3, returning a function that takes a Focus.traversable and a new value.

Examples

iex> alias Focus.Lens
iex> name_setter = Lens.make_lens(:name)
...> |> Lens.fix_set
iex> %{name: "Bart", parents: {"Homer", "Marge"}}
...> |> name_setter.("Lisa")
%{name: "Lisa", parents: {"Homer", "Marge"}}
fix_view(lens)
fix_view(Focus.Lens.t) :: (Focus.traversable -> any)

Fix Lens.view!/2 on a given lens. This partially applies Lens.view/2 with the given lens and returns a function that takes a Focus.traversable structure.

Examples

iex> alias Focus.Lens
iex> view_name = Lens.make_lens(:name)
...> |> Lens.fix_view
iex> homer = %{name: "Homer"}
iex> view_name.(homer)
"Homer"
iex> [homer, %{name: "Marge"}, %{name: "Bart"}]
...> |> Enum.map(&view_name.(&1))
["Homer", "Marge", "Bart"]
make_lens(path)
make_lens(list) :: Focus.Lens.t

Define a lens to focus on a part of a data structure.

Examples

iex> alias Focus.Lens
iex> person = %{name: "Homer"}
iex> name_lens = Lens.make_lens(:name)
iex> name_lens.get.(person)
"Homer"
iex> name_lens.put.(person).("Bart")
%{name: "Bart"}
over(lens, structure, f)

Modify the part of a data structure that a lens focuses on.

Examples

iex> alias Focus.Lens
iex> marge = %{name: "Marge", address: %{street: "123 Fake St.", city: "Springfield"}}
iex> name_lens = Lens.make_lens(:name)
iex> Lens.over(name_lens, marge, &String.upcase/1)
%{name: "MARGE", address: %{street: "123 Fake St.", city: "Springfield"}}
set(lens, structure, val)

Update the part of a data structure the lens focuses on.

Examples

iex> alias Focus.Lens
iex> marge = %{name: "Marge", address: %{street: "123 Fake St.", city: "Springfield"}}
iex> name_lens = Lens.make_lens(:name)
iex> Lens.set(name_lens, marge, "Homer")
%{name: "Homer", address: %{street: "123 Fake St.", city: "Springfield"}}

iex> alias Focus.Lens
iex> marge = %{name: "Marge", address: %{street: "123 Fake St.", city: "Springfield"}}
iex> address_lens = Lens.make_lens(:address)
iex> street_lens = Lens.make_lens(:street)
iex> composed = Lens.compose(address_lens, street_lens)
iex> Lens.set(composed, marge, "42 Wallaby Way")
%{name: "Marge", address: %{street: "42 Wallaby Way", city: "Springfield"}}
view(lens, structure)
view(Focus.Lens.t, Focus.traversable) ::
  {:error, :bad_arg} |
  {:ok, any}

Get a piece of a data structure that a lens focuses on; returns {:ok, data} | {:error, :bad_lens_path}

Examples

iex> alias Focus.Lens
iex> marge = %{name: "Marge", address: %{street: "123 Fake St.", city: "Springfield"}}
iex> name_lens = Lens.make_lens(:name)
iex> Lens.view(name_lens, marge)
{:ok, "Marge"}
view!(lens, structure)
view!(Focus.Lens.t, Focus.traversable) :: any | nil

Get a piece of a data structure that a lens focuses on.

Examples

iex> alias Focus.Lens
iex> marge = %{name: "Marge", address: %{street: "123 Fake St.", city: "Springfield"}}
iex> name_lens = Lens.make_lens(:name)
iex> Lens.view!(name_lens, marge)
"Marge"
x ~> y

Infix lens composition

Examples

iex> import Focus.Lens
iex> alias Focus.Lens
iex> marge = %{name: "Marge", address: %{
...>   local: %{number: 123, street: "Fake St."},
...>   city: "Springfield"}
...> }
iex> address_lens = Lens.make_lens(:address)
iex> local_lens = Lens.make_lens(:local)
iex> street_lens = Lens.make_lens(:street)
iex> address_lens ~> local_lens ~> street_lens |> Lens.view!(marge)
"Fake St."