README.org

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

#+ATTR_HTML: :style margin-left: auto; margin-right: auto;
[[img/focus_lens_prism.png]]

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

#+BEGIN_QUOTE
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.
  -- [[https://docs.racket-lang.org/lens/lens-intro.html][Racket 'lens' documentation]] 
#+END_QUOTE 

** Usage

To construct a lens:

#+BEGIN_SRC elixir
  # 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)
#+END_SRC

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:

#+BEGIN_SRC elixir
  # 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"}
#+END_SRC

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

#+BEGIN_SRC elixir
  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: "???"
  #   }
  # }
#+END_SRC

** Macros
*** Optic creation
  + =deflenses= :: A wrapper around =defstruct= that additionally defines lenses for the struct's keys inside the module. 
      #+BEGIN_SRC elixir
        defmodule User do
          import Lens
          deflenses name: nil, age: nil

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

** 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=:

    #+BEGIN_SRC elixir
      def deps do
        [{:focus, "~> 0.3.5"}]
      end
    #+END_SRC

** References
+ [[https://www.schoolofhaskell.com/user/tel/a-little-lens-starter-tutorial][A Little Lens Starter Tutorial]]
+ [[https://github.com/ekmett/lens/wiki/FAQ#lens-resources][ekmett/lens package FAQ]]
+ [[https://hackage.haskell.org/package/lens-tutorial-1.0.2/docs/Control-Lens-Tutorial.html][Control.Lens Tutorial]]

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