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 10 to 1

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.

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

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

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

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

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]]}
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.

josevalim

josevalim

Creator of Elixir

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

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

Sebb OP

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

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).

— All posts loaded —

Where Next? Top

Trending in Questions Top

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
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
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
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
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews