zachdaniel
Creator of Ash
Dynamically generate typespecs from module attribute list
I was hoping I could get something like this working:
defmodule FooRegistry do
@foos [
FirstFoo,
SecondFoo,
ThirdFoo
]
@foos_by_another_name Enum.into(foos, %{}, fn foo -> {foo.another_name(), foo} end)
@type foo [Enum.map(@foos, fn foo -> foo.t end)]
@spec list_foos() :: [foo]
def list_foos() do
@foos
end
@spec foo_from_another_name(atom) :: foo | nil
def foo_from_another_name(name) do
Map.get(@foos_by_another_name, name)
end
end
I’m not really worried about the exact syntax, but how might I go about dynamically generating a type like this?
Thanks!
Most Liked
evadne
For posterity:
defmodule Test do
@keys ~w(a b c)a
@type key :: unquote(Enum.reduce(@keys, &{:|, [], [&1, &2]}))
end
21
axelson
Scenic Core Team
This is a great snippet! Here’s a function form of this:
defmodule TypeUtils do
# https://forum.elixirforum.com/t/dynamically-generate-typespecs-from-module-attribute-list/7078/5
def list_to_typespec(list) when is_list(list) do
Enum.reduce(list, &{:|, [], [&1, &2]})
end
end
Usage example:
@statuses [:not_started, :completed]
@type status :: unquote(TypeUtils.list_to_typespec(@statuses))
5
Eiji
In both @spec and @type you need to wrap everything in unquote call. Without that instead of calling function/macro the AST of such call would be stored:
iex> quote do
iex> union_type(@keys)
iex> end
{:union_type, [],
[{:@, [context: Elixir, import: Kernel], [{:keys, [context: Elixir], Elixir}]}]}
__using__/1 macro is as same as any other macro. use MyModule works like require MyModule + MyModule.__using__([]).
You can also write a macro like this one:
defmodule UnionType do
defmacro union_type({:"::", _, [{name, _, _}, data]}) do
quote bind_quoted: [data: data, name: name] do
@type unquote({name, [], Elixir}) :: unquote(UnionType.union_type_ast(data))
end
end
def union_type_ast([item]), do: item
def union_type_ast([head | tail]), do: {:|, [], [head, union_type_ast(tail)]}
end
defmodule Example do
import UnionType
@keys ~w(a b c)a
union_type key :: @keys
end
However for this you need to add this option:
[
# …
locals_without_parens: [union_type: 1]
]
to .formatter.exs, because otherwise it would format your code to:
defmodule Example do
import UnionType
@keys ~w(a b c)a
union_type(key :: @keys)
end
4
Last Post!
kenny-evitt
Popular in Questions
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
Hi there,
I am working with Ecto-Postgresql and I need to call all of the records from a specific table but the table has 40,000 records...
New
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
Hi,
I have to write a raw query for one of my project. But till now I have used ecto queries and don’t have much experience writing raw ...
New
Hello everyone,
I try to use an Javascript Event Handler in my root.html.leex file.
Therefore I created a function in the app.js file: ...
New
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service.
Currently when I de...
New
Other popular topics
Hi guys, i’m new in the Elixir world, and i have to say, that i love it!
i’m having some problem to understand anonymous functions with ...
New
What is the proper way to load a module from a file in to IEX?
In the python world, doing something like this pretty standard:
from ....
New
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
What is the idiomatic way of matching for not nil in Elixir?
E.g.,
First way:
defp halt_if_not_signed_in(conn, signed_in_account) when...
New
Hello!
tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability.
After spen...
New
Hello everyone,
Long time lurker first time poster here. I’ve recently begun working on Elixir full-time again! :raised_hands: It’s been...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #elixirconf-us
- #advent-of-code
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #hex
- #security









