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
jonnycharles
I’m in search of an Elixir library that offers PDF generation capabilities similar to Ruby’s Prawn. While there have been discussions abo...
New
spammy
I’m looking to build a personal workflow to quickly deploy web applications written in elixir/phoenix, for local consumption (ie not on t...
New
Blokh
Hey guys, I’ve got a huge CSV ( around 10 GB ) that needs to be processed hourly Do you guys have any suggestions what is the best prac...
New
dli
Before I dive in myself, did anyone successfully sprinkle Hologram into their existing LiveView app? Looking for hints regarding: Addi...
New
roeland
Kia ora, We have been using elixir-google-api to connect to Google Drive. However, with the updates to Tesla due to CVEs this is now bro...
New
subsaharancoder
I’ve followed the Phoenix LiveView file upload code here Uploads — Phoenix LiveView v1.0.0-rc.7 and so far everything works just fine wit...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
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
mcass19
ExRatatui lets you cook up rich terminal UIs in Elixir, powered by Rust’s ratatui via Rustler NIFs. Build interactive terminal applicatio...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Damirados
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge &amp; Solve. They are GUI (Emerge) and State management (S...
New
ausimian
Emily is an Elixir library that runs Nx computations on Apple’s MLX. Install it as the default Nx backend and Nx, defn, Axon, Nx.Serving,...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews