1
votes

I am learning Elixir (have some exp in Ruby, JS). Have an interesting question. Just out of curiosity and for better understanding of the functional programming.

From the documentation:

if(condition, clauses)

Provides an if/2 macro. This macro expects the first argument to be a condition and the second argument to be a keyword list.

In other words I can write:

if(true, [do: "true stuff", else: "false stuff"]) # => "true stuff"

And this line is working perfectly. Ok.

But as far as I aware keywords lists are lists of tuples. So I've tried:

if(true, [{:do, "true stuff"}, {:else, "false stuff"}]) # => "true stuff"

Still working!

Good. Lets do next step:

var = "true stuff"
if( true, [{:do, var}, {:else, "false stuff"}]) # => "true stuff"

Nice. Now lets be crazy!

var = {:do, "true stuff"}
if( true, [var, {:else, "false stuff"}])

** (ArgumentError) invalid or duplicate keys for if, only "do" and an optional "else" are permitted
   (elixir) lib/kernel.ex:2569: Kernel.build_if/2
   (elixir) expanding macro: Kernel.if/2
            iex:18: (file)

Oops!

And if we just try it in 'iex' everything is Ok:

var = {:do, "true stuff"}
[var] # => [do: "true stuff"] 

Why Elixir's 'if' macros don't "recognize" this "good to eat tuple" ?

Was such a thing made by purpose ?

1

1 Answers

3
votes

if macro is a macro, accepting a keyword list parameters. There are two possible variants allowed: only do key is presented and both do and else are presented (4 lines below.)

All other calls do directly match to the clause, that raises an exception you are seeing.

Now let’s understand how keyword lists work:

iex> quote do: [a: 42]
[a: 42]
iex> quote do: [{:a, 42}]
[a: 42]

They are the same! But nobody would decode var for you:

iex> var = [a: 42]
iex> quote do: var
{:var, [], Elixir}

do clause has a special meaning in Elixir, since one might pass a block with it, that’s why it’s handled before all macro expansions and one can’t even embed the keyword list as you’ve tried with another macro.