Sebb

Sebb

I can do:

if true, do: :foo, else: :bar

and:

if true do
  :foo
else
  :bar
end

And I can do:

for i <- 0..3, do: {i, i+i}, into: %{}

but not:

for i <- 0..3 do
  {i, i+i}
into
  %{}
end

:thinking:

why is that?

Showing Posts 1 to 10

hauleth

hauleth

Because else got special treatment to ease user experience. This sugar is not available for any other atom outside the small set of predefined ones (else, rescue, catch, and after).

Sebb

Sebb OP

So I can’t use into, uniq, reduce in a for ... end block?

josevalim

josevalim

Creator of Elixir

You can, you have to pass them before the do.

for …, into: %{} do
  …
end
13
Post #3
chungwong

chungwong

if true, do: :foo, else: :bar

and:

if true do
  :foo
else
  :bar
end

are different, although they look similar.

The first form is actually a if macro accepting two arguments.

if(true, [do: :foo, else: :bar])

And to make the second form works, we need a into macro which I don’t think we have it.

hauleth

hauleth

That is not true. Both are exactly the same thing for the compiler:

iex(1)> quote do
...(1)> if true do
...(1)>   :foo
...(1)> else
...(1)>   :bar
...(1)> end
...(1)> end
{:if, [context: Elixir, import: Kernel], [true, [do: :foo, else: :bar]]}
iex(2)> quote do
...(2)> if(true, [do: :foo, else: :bar])
...(2)> end
{:if, [context: Elixir, import: Kernel], [true, [do: :foo, else: :bar]]}
lud

lud

Yes they are the same. A do block after a call adds the [do: ...] argument to the function/macro call on the left.

So if you call this (notice the parentheses for the if call)

if(a == 1) do
  :ok
end

The compiler kind of rewrites it to if(a == 1, [do: :ok])

This works for functions too:

defmodule Test do
  def myfun(value, block) do
    value |> IO.inspect(label: "value")
    block |> IO.inspect(label: "block")
  end
end

Test.myfun :some_value do
  a = 1
  b = 2
  a + b
end

This will output the following:

value: :some_value
block: [do: 3]

But as myfun/2 is a function and not a macro, the block is evaluated before being passed to the function, hence you see [do: 3], whereas with macros the quoted block is passed. This syntactic sugar is then less useful for functions.

The for macro is special, because if you pass into: %{}, do: stuff(), the do and into are in the same keyword list ; if you pass into: %{} and then a do end block, the compiler will add another argument to the for() call, containing just the do block:

iex(1)> quote do
...(1)> for a <- 1..2, into: %{} do
...(1)> {a, a*a} 
...(1)> end
...(1)> end
{:for, [],
 [
   {:<-, [],
    [{:a, [], Elixir}, {:.., [context: Elixir, import: Kernel], [1, 2]}]},
   [into: {:%{}, [], []}],
   [
     do: {{:a, [], Elixir},
      {:*, [context: Elixir, import: Kernel],
       [{:a, [], Elixir}, {:a, [], Elixir}]}}
   ]
 ]}
iex(2)> quote do                   
...(2)> for a <- 1..2, into: %{}, do: {a, a*a}
...(2)> end
{:for, [],
 [
   {:<-, [],
    [{:a, [], Elixir}, {:.., [context: Elixir, import: Kernel], [1, 2]}]},
   [
     into: {:%{}, [], []},
     do: {{:a, [], Elixir},
      {:*, [context: Elixir, import: Kernel],
       [{:a, [], Elixir}, {:a, [], Elixir}]}}
   ]
 ]}

I guess that is why for is defined in Kernel.SpecialForms as an error because it is handled at the compiler level.

hauleth

hauleth

Well, no. It is still consistent behaviour among all other calls:

quote do
  foo bar: 1, do: 10
end
# => {:foo, [], [[bar: 1, do: 10]]}

quote do
  foo bar: 1 do
    10
  end
end
# => {:foo, [], [[bar: 1], [do: 10]]}

There are 2 reasons why for is special form and not “regular” macro:

  • Compiler need to know that it is comprehension, so it can output Erlang comprehension as well
  • <<a <- binary>> syntax need special handling
  • Macros do not support variable arguments list (the same rules as any other call apply there as well)

So all of the above forces the Elixir to treat for as a “special thing” within parser itself.

lud

lud

I don’t understand to what you say “no”.

I mean in your example it calls two different foo functions. The for construct handles that in a special way, there are not several definitions for for to my knowledge in the Elixir code.

hauleth

hauleth

As I said, it is literally impossible to define for in Elixir. I meant that do: 10 and do … end aren’t really treated differently just for for, but the same rules applies to all calls.

for is special form not because of do but because each x <- y is separate argument, so for i <- 1..10, do: i is 2-ary function and for i <- 1..10, j <- 1..10, do: i + j is 3-ary function. So either for would need to have N different heads and just fail in case if someone would like to use more than that, then it would fail (see older Rust compilers to see that problem, there Debug is defined only up to 32-ary tuples, as there is no variadic generics). Due that problem for and with must be defined as special forms, because they require parser magic to be then expanded properly.

lud

lud

Ok so it is not defined as a special form just for the arity problem, but otherwise it is just what I said, basically it is a special form because it requires special treatment at the compiler level.

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
bradley
I really like the adapter patterns that ecto, nebulex, waffle, etc. use and would love find something similar for a key management servic...
New
unaware8150
Hello folks! So at work, we are seeing some situations where we have to define some “fixed” strings that are used across the codebase in...
New
Alvinkariuki
How Can I Optimise Compile Time Dependencies I have been building an elixir application for about 2 years now. Many modules and files ha...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New

Other Trending Topics Top

CharlesIrvine
I just published a free course on building agents using Elixir. It’s at: Feedback would be appreciated. Cheers, Chuck
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
juhalehtonen
There has been a thread to discuss the Stack Overflow Developer Survey on this forum every year since 2018, so here’s yet another one for...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews