Skip to content

Instantly share code, notes, and snippets.

@lafka
Last active August 29, 2015 14:05
Show Gist options
  • Save lafka/0c7fc4cc558c6b6a0bb8 to your computer and use it in GitHub Desktop.
Save lafka/0c7fc4cc558c6b6a0bb8 to your computer and use it in GitHub Desktop.
Macro for modifying deep lists
defmodule Lens do
@moduledoc """
Utility to dynamically put nested keys into maps
```
import Lens, stringkeys?: false
a = %{}
a.b.c.d <- "i'm a dddddd"
IO.inspect a # => %{b: %{c: %{d: "i'm a dddddd}}}
```
"""
defmacro __using__(options \\ []) do
keyfun = case options[:stringkeys?] do
true ->
quote(do: &Atom.to_string/1)
_ ->
quote(do: fn(a) -> a end)
end
quote do
defmacro left <- right do
[var | path] = Lens.compactpath(left, [], unquote(keyfun))
setter = Lens.gensetpath(var, path, right)
quote(do: unquote(var) = unquote(setter))
end
end
end
@doc """
Helper to calculate the path of the setter reference
"""
def compactpath({{:., _, [Access, :get]}, _, [a, [_|_] = b]}, [], keyfun), do:
compactpath(a, b, keyfun)
def compactpath({{:., _, [Access, :get]}, _, [a, b]}, [], keyfun), do:
compactpath(a, [b], keyfun)
def compactpath({{:., _, [a,b]}, _, _}, [], keyfun), do:
compactpath(a, [keyfun.(b)], keyfun)
def compactpath({{:., _, [Access, :get]}, _, [a, b]}, acc, keyfun), do:
compactpath(a, [b|acc], keyfun)
def compactpath({{:., _, [a,b]}, _, _}, acc, keyfun), do:
compactpath(a, [keyfun.(b) | acc], keyfun)
def compactpath([b | rest], acc, keyfun), do:
compactpath(rest, [b|acc], keyfun)
def compactpath(b, acc, _keyfun) do
[b|acc]
end
@doc """
Helper to generate setter function AST
"""
def gensetpath(var, [k], v), do:
quote(do: Dict.put(unquote(var), unquote(k), unquote(v)))
def gensetpath(var, [k|rest], v) do
nest = gensetpath(quote(do: unquote(var)[unquote(k)] || %{}), rest, v)
quote(do: Dict.put(unquote(var), unquote(k), unquote(nest)))
end
def setpath(var, [k], v), do:
Dict.put(var, k, v)
def setpath(var, [k|rest], v), do:
Dict.put(var, k, setpath(var[k] || %{}, rest, v))
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment