sfrances

sfrances

Using module attributes in typespec definitions to reduce duplication?

Hi

I’m trying to use module attributes in my typespec, but I get errors when I do this:

defmodule KitchenCalculator do
  @ml :milliliter
  @cup :cup
  @fluid_ounce :fluid_ounce
  @tsp :teaspoon
  @tbsp :tablespoon

  @type unit :: @ml | @cup | @fluid_ounce | @tsp | @tbsp

  # More stuff here
end

I get the error:

** (CompileError) lib/kitchen_calculator.ex:8: type ml/0 undefined (no such type in KitchenCalculator)
    (elixir 1.12.2) lib/kernel/typespec.ex:925: Kernel.Typespec.compile_error/2
    (stdlib 3.15.2) lists.erl:1358: :lists.mapfoldl/3
    (elixir 1.12.2) lib/kernel/typespec.ex:834: Kernel.Typespec.typespec/4
    (stdlib 3.15.2) lists.erl:1358: :lists.mapfoldl/3
    (elixir 1.12.2) lib/kernel/typespec.ex:464: Kernel.Typespec.typespec/4
    (elixir 1.12.2) lib/kernel/typespec.ex:307: Kernel.Typespec.translate_type/2
    (stdlib 3.15.2) lists.erl:1358: :lists.mapfoldl/3
    (elixir 1.12.2) lib/kernel/typespec.ex:235: Kernel.Typespec.translate_typespecs_for_module/2

I use the module attributes later on, to reduce duplication and chance of errors via typos when typing out atoms. Is there any way to achieve this, or do I just live with the minor duplication?

Marked As Solved

kip

kip

ex_cldr Core Team

You can do it like this:

defmodule KitchenCalculator do
  @ml :milliliter
  @cup :cup
  @fluid_ounce :fluid_ounce
  @tsp :teaspoon
  @tbsp :tablespoon

  types = Enum.reduce([@ml, @cup, @fluid_ounce, @tsp, @tbsp], &({:|, [], [&1, &2]}))
  @type unit :: unquote(types)

  # More stuff here
end

You could of course encapsulate the Enum.reduce/2 into a macro and reuse it that way.

iex> t KitchenCalculator.unit
@type unit() :: :tablespoon | :teaspoon | :fluid_ounce | :cup | :milliliter

BTW, if you’re spending a lot of time with units, unit math and unit conversions, and potentially unit serialisation and localization, you might find ex_cldr_units helpful (I’m the author).

Also Liked

kip

kip

ex_cldr Core Team

In the few occasions when I’ve used the technique above I have done it directly. In my cases, building a macro just to do that hasn’t been important and I felt it would actually make the code more obscure. By having the Metaprogramming line directly above, it makes it easy for “future me” to work out what I was thinking.

I’m sure you’ve heard the aphorism in Elixir-land, “first rule of macros is don’t use macros”.

Where Next?

Popular in Questions Top

lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
nobody
How to bind a phoenix app to a specific ip address? could not find anything about that, nowhere, unfortunately, but for me this is quite...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New
joeerl
Hello again - after a longish gap I’ve decided I really must dig into Elixir and see what’s been happening here - so I have a few questio...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
vonH
When I run the Plug and I recompile I wind up having to use Ctrl C to quit iex and start again. Witht the help of rlwrap I can use the cu...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
joaquinalcerro
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
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
jononomo
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

Other popular topics Top

aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
AstonJ
Posting this to see if we can make things easier for people to get into Neovim. If you use Neovim and have a favourite distro please let ...
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
AngeloChecked
What learn first? Rust or Elixir Hi Elixir community! I’m here because i want learn a new language. I’m a junior developer and mainly i ...
New
stefanluptak
Hello everybody, usually, I use a 29" ultra-wide monitor for VSCode which can easily accomodate explorer (files panel) + file with code ...
New
SoCreat
i’m a new one to elixir which editor can i use vs code? or atom? Thanks! :smiley:
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
Brian
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
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New

We're in Beta

About us Mission Statement