Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kernel.--/2 weird behaviour

Tags:

elixir

Consider the following code:

iex|1 ▶ [:foo, :bar] -- [:foo, :bar]
#⇒ []

So far, so good. But:

iex|2 ▶ [:foo, :bar] -- [] -- [:foo, :bar]
#⇒ [:foo, :bar]

Even more, it’s not about right-to-left:

iex|3 ▶ [:foo, :bar] -- [:foo] -- [:foo, :bar] 
#⇒ [:foo, :bar]

iex|4 ▶ IO.inspect([:foo, :bar], label: "1") --
...|4 ▶   IO.inspect([:foo], label: "2") --
...|4 ▶   IO.inspect([:foo, :bar], label: "3")
#⇒ 1: [:foo, :bar]
#  2: [:foo]
#  3: [:foo, :bar]

#⇒ [:foo, :bar]

Am I missing something obvious? What’s going on here? There should not be any magic, since Kernel.--/2 is simply delegating to :erlang.--(left, right).

Why consecutive subtracting lists results in noop?


FWIW, with parentheses everything works as expected:

iex|5 ▶ ([:foo, :bar] -- [:foo]) -- [:foo, :bar]
#⇒ []

More fun:

iex|6 ▶ [:foo, :bar] -- [:foo] -- []  
#⇒ [:bar]
iex|7 ▶ [:foo, :bar] -- [:foo] -- [:foo]
#⇒ [:foo, :bar]
iex|8 ▶ [:foo, :bar] -- [:foo] -- [:bar]
#⇒ [:bar]

Follow-up findings. Short-form reducing somehow manages to follow right-associative semantics:

Enum.reduce([[:foo, :bar], [:foo], [:foo, :bar]], &Kernel.--/2)
#⇒ [:foo, :bar]

But the full well-formed one with an explicit function does not

Enum.reduce(
  [[:foo, :bar], [:foo], [:foo, :bar]],
  fn e, acc -> acc -- e end
)
#⇒ []
like image 462
Aleksei Matiushkin Avatar asked Feb 04 '19 20:02

Aleksei Matiushkin


1 Answers

++ and -- are right-associative operations. Your initial code:

[:foo, :bar] -- [:foo] -- [:foo, :bar]

Is actually evaluated as

[:foo, :bar] -- ([:foo] -- [:foo, :bar])

When you take what's in the brackets: you remove a list of elements from [:foo], and the list you remove contains the original list ([:foo]) thus evaluates to an empty list [].

Then you remove this empty list from the leftmost list:

[:foo, :bar] -- []

Which leaves you with the result [:foo, :bar].

like image 142
Máté Avatar answered Oct 10 '22 20:10

Máté