Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

49 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Lenses

Lenses is an Erlang port of the Haskell library lens.

lens

It currently implements a focused subset of the upstream API.

Lens

type Lens s t a b = forall f. Functor f => (a -> f b) -> (s -> f t)
lens:lens(s -> a, s -> b -> t) -> Lens s t a b

Lens = lens:lens(fun({_, A}) -> A end, fun({C, _}, B) ->  {C, B} end),
?assertEqual(world, getter:view(Lens, {hello, world})),
?assertEqual({hello, another_world}, setter:set(Lens, another_world, {hello, world})).

lens:'_1'(), lens:'_2'()          % tuple element lenses
lens:at(Key)                      % map key lens (Maybe a)
lens:choosing(L, R)               % combine two optics over an Either
lens:alongside(L, R)              % combine two optics over a pair
lens:clone_lens(Lens)

Traversal

type Traversal s t a b = forall f. Applicative f => (a -> f b) -> (s -> f t)
traversal:traverse() -> forall t. Traversable t => Traversal (t a) (t b) a b

Traversal = traversal:traverse(),
?assertEqual([2, 4], setter:over(Traversal, fun(A) -> A + 1 end, As)).

traversal:both()            % traversal over both elements of a 2-tuple/list
traversal:each()            % traversal over list/map/tuple elements
traversal:ix(Index)         % traversal over the element at index/key
traversal:beside(L, R)      % combine two traversals over a pair
traversal:elements(Pred)    % traversal filtered by element index
traversal:take(N), traversal:drop(N)
traversal:clone_traversal(Traversal)

ISO

type Iso s t a b = forall p f. (Profunctor p, Functor f) => p a (f b) -> p s (f t) 
iso:iso(s -> a, b -> t) -> Iso s t a b
iso:from(Iso s t a b) -> Iso b a t s

Iso = iso:iso(fun(A) -> identity:run(A) end, fun(A) -> identity:identity(A) end),
?assertEqual(3, getter:view(Iso, {identity, 3})),
?assertEqual({identity, 1}, setter:set(Iso, 1, {identity, 3})),
?assertEqual({identity, 3}, getter:view(iso:from(Iso), 3)).

iso:under(Iso, AB, S)       % over through the reverse iso
iso:mapping(Iso)            % lift an Iso through a Functor

Prism

type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
prism:prism(b -> t, s -> Either t a) -> Prism s t a b
prism:matching(Prism s t a b, s) -> Either t a
prism:preview(Prism s t a b, s) -> Maybe a
prism:review(Prism s t a b, b) -> t

-include_lib("erlando/include/op.hrl").
Prism = prism:prism(fun(A) -> {cat, A} end, fun({cat, A}) -> {right, A}; (Other) -> {left, Other} end),
Traverse = traversal:traverse(),
Compose = Traverse /'.'/ Prism,
CatsAndDogs = [{cat, kitty}, {dog, snoopy}, {cat, coffee}],
?assertEqual([kitty, coffee], fold:to_list_of(Compose, CatsAndDogs)),
?assertEqual([{cat, {my, kitty}}, {dog, snoopy}, {cat, {my, coffee}}],
              setter:over(Compose, fun(Cat) -> {my, Cat} end, CatsAndDogs)),
?assertEqual({right, kitty}, prism:matching(Prism, {cat, kitty})),
?assertEqual({just, kitty}, prism:preview(Prism, {cat, kitty})),
?assertEqual({cat, kitty}, prism:review(Prism, kitty)).

prism:just(), prism:nothing()   % Maybe prisms
prism:left(), prism:right()     % Either prisms
prism:head()                    % list head prism
prism:only(A)                   % equality prism
prism:below(Prism)              % lift a prism through a Traversable
prism:re(Prism)                 % reverse the direction of a prism
prism:clone_prism(Prism)

Getter

type Getting r s a = (a -> Const r a) -> (s -> Const r s)
type Getter s a = forall r. Getting r s a
getter:view(Getting a s a, s) -> a
getter:to(F) -> Getter           % build a Getter from a plain function

Fold

fold:to_list_of(Getting [a] s a, s) -> [a]
fold:folding(F) -> Fold          % build a Fold from a function into a Foldable

fold:length_of/2, fold:sum_of/2, fold:product_of/2
fold:max_of/2, fold:min_of/2
fold:any_of/3, fold:all_of/3, fold:none_of/3
fold:elem_of/3, fold:not_elem_of/3, fold:find_of/3
fold:first_of/2, fold:last_of/2
fold:has/2, fold:has_not/2, fold:preview/2

Setter

type Setter s t a b = (a -> Identity b) -> (s -> Identity t)
setter:over(Setter s t a b, a -> b, s) -> t
setter:setting(F) -> Setter      % build a Setter from ((a -> b) -> s -> t)
setter:mapped()                  % Setter over any Functor

TypeClass & Instances

  • function (->) is an instance of Profunctor
  • Const r is an instance of Functor
  • Identity is an Instance of Functor
  • Choice is Profunctor
  • Applicative is Functor
  • Lens is Getter
  • Lens is Traversal
  • Traversal is Setter
  • Traversal is Fold
  • Iso is Lens
  • Iso is Prism
  • Prism is Traversal
  • Getter is Fold

Compose

Lenses can be composed with function composition because

(p a (f b) -> p s (f t)) -> (p x (f y) -> p a (f b)) -> (p x (f y) -> p s (f t))

MakeLenses

-compile({parse_transform, make_lenses}).
-record(state, {hello, world}).
-make_lenses([state, #{module => state}]).

generates

state:hello/0
state:world/0

which represents lenses of #state.hello, #state.world

Multiple records can be declared in a single attribute:

-make_lenses([state, local_state]).

MakePrisms

-compile({parse_transform, make_prisms}).
-make_prisms([{cat, [{cat, 1}, {dog, 1}]}]).

generates prisms for the tagged-tuple constructors {cat, A} and {dog, A}:

cat() :: Prism {cat, A} | {dog, A} A
dog() :: Prism {cat, A} | {dog, A} A

Nullary constructors use arity 0, and higher-arity constructors focus on the payload tuple. Output to a separate module is supported via #{module => Mod}.

Laws

User-defined optics are expected to satisfy the corresponding upstream lens laws. The test suite covers the Lens get-put, put-get and put-put laws, Setter identity and composition, Prism review/preview round trips, Iso inversion, and the new prism optic and container optic laws.

Releases

Packages

Contributors

Languages