irresponsible/polylens.ex
{ "createdAt": "2018-06-27T14:57:20Z", "defaultBranch": "master", "description": "Polymorphic Lenses for fun and profit", "fullName": "irresponsible/polylens.ex", "homepage": null, "language": "Elixir", "name": "polylens.ex", "pushedAt": "2018-09-02T15:36:18Z", "stargazersCount": 11, "topics": [], "updatedAt": "2023-09-01T10:46:37Z", "url": "https://github.com/irresponsible/polylens.ex"}Polylens
Section titled “Polylens”An elixirified port of Haskell’s lenses using multiple-dispatch polymorphism.
alias Polylens.Lenses
def sample, do: {1, %{2 => [3, 4]}}
# These lenses address each of the numbersdef one, do: [Lenses.at_index(0)]def two, do: [Lenses.at_index(1), Lenses.key_at(2)]def three, do: [Lenses.at_index(1), Lenses.at_key(2), Lenses.at_index(0)]def four, do: [Lenses.at_index(1), Lenses.at_key(2), Lenses.at_index(1)]
def numbers, do: [four, three, two, one]
def example do
# Firstly, we can get the values they lens over for number <- numbers do {:ok, num} = Polylens.get_in(number, sample) IO.inspect(num) end
# We can set them all to the same thing. Result: {42, %{42 => [42, 42]}} Enum.reduce(numbers, sample, fn lens, data -> {:ok, ret} = Polylens.set_in(lens, data, 42) ret end |> IO.inspect()
# We can modify them all. Result: {2, %{3 => [4, 5]}} Enum.reduce(numbers, sample, fn lens, data -> {:ok, ret} = Polylens.update_in(lens, data, fn x -> x + 1 end) ret end |> IO.inspect()
endInstallation
Section titled “Installation”If available in Hex, the package can be installed
by adding polylens to your list of dependencies in mix.exs:
def deps do [ # ... other deps ... {:polylens, "~> 0.1.0"}, ]endImplementing your own lenses
Section titled “Implementing your own lenses”We use protocol_ex to fake
multiple dispatch with {lens, data} tuples via the Lens protocol_ex.
To build your own lenses, you will need to add
:protocol_ex to your Mix project compilers:
def project do [ ## ... other config ... ## Make sure [:protocol_ex] comes after :elixirc ! compilers: Mix.compilers ++ [:protocol_ex], ]endYou may then implement the Lens protocol_ex. Here is how we
implement AtKey for maps:
import ProtocolExalias Polylens.Lens
defimpl_ex MapAtKey, {%Polylens.AtKey{},map} when is_map(map), for: Lens do def get({%{key: key}, map}) do fail = make_ref() case Map.get(map, key, fail) do ^fail -> {:error, :not_found} ret -> {:ok, ret} end end def set({%{key: key}, map}, value), do: {:ok, Map.put(map, key, value)}endNote for Haskell/Purescript users
Section titled “Note for Haskell/Purescript users”If you’re familiar with haskell or purescript lenses, Polylens lenses most closely represent ‘at’ lenses because owing to the dynamic nature of Elixir, they may fail.
Changelog
Section titled “Changelog”- Version 0.1.0 (2018-09-02) Initial release
Copyright and License
Section titled “Copyright and License”Copyright 2018 James Laver
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.