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

tduccuong
Hi, is there any work on GUI with Elixir, that is similar to Electron/Javascript? My idea is to bundle Phoenix and BEAM into a single se...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list....
New
gonzofish
I’m currently trying to understand how to join three tables using Ecto. All the examples I’ve seen use 2, so maybe I’m just missing somet...
New
ovidiubadita
Hey all, I discovered Elixir and I love it. I always wanted to learn a functional programming and I intended to go for Haskell, but afte...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
Codball
Mix format works fine if run from the cmd. I’ve followed this to facilitate the implementation into VSC which involves downloading an ext...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
idi527
I’ve been re-reading swift book again and noticed that multiline strings there don’t have a trailing line break, unlike in elixir iex(2)...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
script
If I have a string “1000 cfu/ml” . I want to remove the characters and / and space . So the string is like this "1000" What is the ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New

We're in Beta

About us Mission Statement