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
Hi!
In PHP: $_SERVER[‘SERVER_ADDR’] - in Elixir?
Searched the docs for ip address and the web, no good results.
Thanks!
New
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
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
For some reason my phoenix channels are working for me in my local dev environment, but as soon as I deploy via Docker, I get a 403 error...
New
If I have a post route which an argument:
post /my_post_route/:my_param1, MyController.my_post_handler
How would get the post params ...
New
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
i’m a new one to elixir
which editor can i use
vs code? or atom?
Thanks! :smiley:
New
Other popular topics
Hi,
I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
Hello, can anybody help here..? I have a list of players and I what to delete an element, but every for loop the list is reverting to ori...
New
I have a phoenix application from which a user can download multiple(5-6) files of size 1MB. I couldn’t find anything related to sending ...
New
This post is a wiki (feel free to hit the edit button near the bottom right of this post to add your own changes!)
This post collects co...
New
Surface is an experimental library built on top of Phoenix LiveView and its new LiveComponent API that aims to provide a more declarative...
New
After calling mix ecto.create I get this error:
17:00:32.162 [error] GenServer #PID<0.412.0> terminating
** (Postgrex.Error) FATAL...
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
- #security
- #hex









