Why is the reverse not matching?

Inside iex

“Idaho” = states[:state]
“Idaho”

but
states[:state] = “Idaho”
** (CompileError) iex:28: cannot invoke remote function Access.get/2 inside a match

What is wrong?

Only left-hand of the = operator is a match pattern, right hand is matched expression. foo[bar] is equivalent to Access.get(foo, bar), and such function is disallowed in match patterns.

2 Likes

Specifically when you have an assigning thing like a = b = c = d the a = b = c part is a match expression and the d part is an evaluatable expression. You can see that if you try the same in, say, a case, where a head is entirely a match expression. I.E. think of something like a = b = c = d as:

case d do
  a = b = c ->
    # *Everything* else in scope after this `a = b = c = d` line
end

Consequently you can now see why:

case "Idaho" do
  states[:state] ->
    ...
end

Won’t work, because the states[:state] is ending up in the match expression. :slight_smile:

2 Likes

This contradicts OvermindDL1’s answer.

This contradicts hauleth’s answer.

No they don’t, they match. ^.^

I.E. the left hand side is the match expression (in repeated ='s the right-most one is the binding operator, the ones to the left of it are all in the match context), and the right side is a normal expression. :slight_smile:

1 Like

No my and @OvermindDL1 are complementing each other. In documentation page about operators there is associativity list where it is stated that = is “right to left”, which mean that:

a = b = c = d

Is the same as

(a = (b = (c = d)))

So that means that all a, b, and c need to be valid patterns.

2 Likes

I think the simple answer is that in Elixir we can not have functions in the left of a pattern match.

1 Like

We can have some functions:

  • Kernel.+/2, Kernel.-/2, Kernel.//2, Kernel.div/2
  • Kernel.is_list/1, Kernel.is_map/1, Kernel.is_function/{1,2}, Kernel.is_binary/1, Kernel.is_tuple/1, Kernel.is_atom/1, Kernel.is_integer/1, Kernel.is_bitstring/1, Kernel.is_boolean/1, Kernel.is_float/1, Kernel.is_number/1, Kernel.is_pid/1, Kernel.is_port/1, Kernel.is_reference/1
  • Kernel.==/2, Kernel.!=/2, Kernel.</2, Kernel.>/2, Kernel.<=/2, Kernel.>=/2
  • Kernel.abs/1
  • Kernel.bit_size/1, Kernel.byte_size/1, Kernel.binary_part/3
  • Kernel.lenght/1
  • Kernel.ceil/1, Kernel.trunc/1, Kernel.round/1
  • Kernel.elem/2
  • Kernel.hd/1, Kernel.tl/1
  • Kernel.map_size/1, :erlang.map_get/2 (AFAIK this one still do not have equivalent in Elixir)
  • Kernel.node/{0,1}
  • Kernel.not/1
  • Kernel.rem/2, Kernel.mod/2
  • Kernel.self/0
  • Kernel.tuple_size/1
  • :erlang.band/2, :erlang.bor/2, :erlang.bxor/2, :erlang.bnot/1, :erlang.bsl/2, :erlang.bsr/2
  • :erlang.and/2, :erlang.or/2, :erlang.andalso/2, :erlang.orelse/2

And of course macros that return AST built only with these, for example Kernel.and/2, Kernel.or/2, or Kernel.is_nil/1.

1 Like

I tried not(true) = false but got this error CompileError) iex:29: cannot invoke remote function :erlang.not/1 inside a match

Yep, specifically any function marked as a ‘guard’ function is useful in the ‘when’ clause of a matchspec.

The left side of a = is a match expression, which is only part of a matchspec.

The head of a case like a when b -> c the a is the match expression, the b is the match guard, together they define a matchspec.

Thus the = cannot use any functions at all.

Macro’s of course, since those are expanded at compile time though.

Yep, that has to go in the guard, so after the when in a matchspec, and simple bindings via = don’t have when guards, that’s what full case usage is for. :slight_smile:

2 Likes

Yeah, not in pattern per se, but in guards. In patterns you can use Kernel.<>/2 macro for sure.

The reason this one works is that a <> b just becomes <<a::binary, b::binary>> via the macro at compile-time. :slight_smile:

And that’s also why a in this example must be a known-length string since you can only match binaries that are known length except for the final field (b in this example), which can be of unknown length. :slight_smile:

I personally view =, the pattern match operator, as something which goes from right-to-left. On the right is an expression and on the left a pattern:

pattern = expression

You first evaluate the expression then you see if the resulting value of that matches the pattern, which says how you want the value to look. If it does then you bind any variables and go on, otherwise you generate an error.

The form pat1 = pat2 = pat3 = expression is exactly the same form. You evaluate the expression then try to match the value against all the patterns. I think we called them “aliases”.

It’s the KISS principle all the way down. :smile:

5 Likes