Why do contexts work differently for aliases/imports and variables?

Consider this example:

iex(1)> defmodule X do
...(1)>   def f(), do: 1
...(1)> end
iex(2)> defmodule X.X do
...(2)>   def f(), do: 1000
...(2)> end
iex(3)> x = 0
0
iex(4)> %{a: x, b: (x = 2; x), c: x}
%{a: 0, b: 2, c: 0}
iex(5)> x
2
iex(6)> X.f
1
iex(7)> %{a: X.f, b: (alias X.X; X.f), c: X.f}
%{a: 1, b: 1000, c: 1000}
iex(8)> X.f
1000

I understand that it’s useful to have variable contexts in map inherited from before-map context and then merged into one after-map contex.

But I don’t understand why alias context is transferred from left to right. I mean, it just makes no sense for me

This one will really bake your noodle :joy:

iex(12)> x = 1
1

iex(13)> %{a: x, b: (x = 2; x), c: (x = x; x)}
%{a: 1, b: 2, c: 1}

iex(14)> x
1

I suspect the core difference is between runtime behavior (the assignments) and compile-time behavior (the aliases).

Yeah, I get the difference, but I do not get the decision behind this difference, and that’s what the question is about

bump, this question is important for me

alias is lexically scoped like bindings are, so I’m wondering if this might actually be a bug/unintended behaviour.

1 Like

require, import, and alias
All of the rules described so far apply to variable bindings. When it comes to one of these three special forms, their effect persists until the end of the do block they are called in. Effectively, those forms see a slightly different scope division in which control flow constructs create a new lexical scope.
Scoping Rules in Elixir (and Erlang) — Elixir documentation

If you want to keep the current scope unaffected, wrap the evaluation in a function so the the effect is limited to the new scope.

iex> %{a: X.f, b: fn -> alias X.X; X.f end.(), c: X.f}
%{a: 1, b: 1000, c: 1}
iex> X.f
1
2 Likes

Thanks, I didn’t even know that this readthedocs documentation existed

I’d love to see this again in current documentation, as readthedocs is really old.