Packages

A functional optics library. Create and compose lenses to view, set, and modify data inside arbitrarily nested maps, lists, and tuples.

Current section

Files

Jump to
focus lib lens lensable.ex
Raw

lib/lens/lensable.ex

defprotocol Lensable do
@fallback_to_any true
@doc "A function to get a value out of a data structure"
def getter(structure, view)
@doc "A function to set a value out of a data structure"
def setter(structure, view, func)
end
defimpl Lensable, for: Map do
def getter(s, x), do: Access.get(s, x, {:error, {:lens, :bad_path}})
def setter({:error, {:lens, :bad_path}} = e), do: e
def setter(s, x, f) do
if Map.has_key?(s, x) do
Map.put(s, x, f)
else
s
end
end
end
defimpl Lensable, for: Tuple do
def getter({:error, _e} = error, _x), do: error
def getter(s, x), do: elem(s, x)
def setter(s, x, f) do
s
|> Tuple.delete_at(x)
|> Tuple.insert_at(x, f)
end
end
defimpl Lensable, for: List do
def getter(s, x) do
if Keyword.keyword?(s) && !Enum.empty?(s) do
Keyword.get(s, x)
else
if is_number(x) && !Enum.empty?(s) do
get_in(s, [Access.at(x)])
else
{:error, {:lens, :bad_path}}
end
end
end
def setter([] = s, x, f), do: List.replace_at(s, x, f)
def setter(s, x, f) do
if Keyword.keyword?(s) do
Keyword.put(s, x, f)
else
List.replace_at(s, x, f)
end
end
end
defimpl Lensable, for: Any do
@bad_data_structure_error {:error, {:lens, :bad_data_structure}}
def getter(s, x) do
case s do
%_type{} -> Lensable.getter(Map.from_struct(s), x)
_ -> @bad_data_structure_error
end
end
def setter(s, x, f) do
case s do
%type{} ->
struct(type, Lensable.setter(Map.from_struct(s), x, f))
_ ->
@bad_data_structure_error
end
end
end