Skip to content
slepherPublic

About

lens in erlang

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Latest commit

 

History

73 Commits

Folders and files

Repository files navigation

CI

CI

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.

About

lens in erlang

Topics

Resources

Stars

6 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages