Skip to content
master
Go to file
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
img
 
 
lib
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.org

Focus

https://circleci.com/gh/tpoulsen/focus.svg?style=svg https://img.shields.io/hexpm/v/focus.svg

img/focus_lens_prism.png

Lightweight, pure Elixir functional optics[fn:1].

A lens is a value that composes a getter and a setter function to produce a bidirectional view into a data structure. This definition is intentionally broad—lenses are a very general concept, and they can be applied to almost any kind of value that encapsulates data. – Racket ‘lens’ documentation

Usage

To construct a lens:

# A lens for the key :name
Lens.make_lens(:name)

# A lens for the key "name"
Lens.make_lens("name")

# A lens for the second item in a tuple:
Lens.make_lens(1)

Each lens provides both a getter and a setter for the accessor it was created for.

Lenses can be used to access and/or modify structured data:

# Extract a value from a simple map:

person = %{name: "Homer"}
nameLens = Lens.make_lens(:name)

Focus.view(nameLens, person) 
# "Homer"

Focus.set(nameLens, person, "Bart")
# %{name: "Bart"}

Focus.over(nameLens, person, &String.upcase/1)
# %{name: "HOMER"}

Their real utility comes in operating on nested data. Lenses can be created by composing other lenses in order to traverse a data structure:

person = %{
  name: "Homer",
  address: %{
    locale: %{
      number: 742,
      street: "Evergreen Terrace",
      city: "Springfield",
    },
    state: "???"
  }
}

# To access the street, we can compose the lenses that lead there from the top level.
# Lenses can be composed with Focus.compose/2, or the infix (~>) operator.

address = Lens.make_lens(:address)
locale =  Lens.make_lens(:locale)
street =  Lens.make_lens(:street)

address
~> locale
~> street
|> Focus.view(person)
# "Evergreen Terrace"

address
~> locale
~> street
|> Focus.set(person, "Fake Street")
# person = %{
#   name: "Homer",
#   address: %{
#     locale: %{
#       number: 742,
#       street: "Fake Street",
#       city: "Springfield",
#     },
#     state: "???"
#   }
# }

Macros

Optic creation

deflenses
A wrapper around defstruct that additionally defines lenses for the struct’s keys inside the module.
defmodule User do
  import Lens
  deflenses name: nil, age: nil

  # deflenses defines %User{}, User.name_lens/0, and User.age_lens/0
end
    

Functions

Optic creation

  • Lens.make_lens/1
  • Lens.make_lenses/1
  • Lens.idx/1

Pre-made optics

  • Prism.ok/0
  • Prism.error/0

Optic application

  • Focus.view/2
  • Focus.over/3
  • Focus.set/3
  • Focus.view_list/2
  • Focus.has/2
  • Focus.hasnt/2
  • Focus.fix_view/2
  • Focus.fix_over/3
  • Focus.fix_set/3

Optic composition

  • Focus.compose/2, (~>)
  • Focus.alongside/2

Installation

  1. Add focus to your list of dependencies in mix.exs:
    def deps do
      [{:focus, "~> 0.3.5"}]
    end
        

References

Footnotes

[fn:1] This library currently combines Lenses and Prisms with Traversals in its implementation. Until v1.0.0, the API is subject to large and frequent change.

You can’t perform that action at this time.