jgonet

jgonet

Function refs in guards

I was creating simple RPN calculator function, but I stumbled upon a problem in matching functions in guards.

Code looks like so:

def compute_RPN_expr(value, stack) when is_number(value), do: [value | stack]

def compute_RPN_expr(operator, stack) when operator in [&+/2, &-/2, &*/2, &//2] do
  [a, b | stack] = stack
  stack = [operator.(b, a) | stack]
end

After trying to define it inside IEX I’m getting invalid expression in guard, & is not allowed in guards.

Why? I can do:

a = &//2
a === &//2

Without much hassle, but I can’t use the same equality test via in.
Is there any Elixir/Erlang limitation or this was simply overlooked?

Marked As Solved

Qqwy

Qqwy

TypeCheck Core Team

One important reason is hot code reloading and the distributed kind of Erlang/Elixir programs: If you are running code on multiple nodes, then they both have separate versions of the functions that might be executed (and if you try to upgrade both nodes to a new version, they are not guaranteed to upgrade at exactly the same time). If you pass on a function reference from one node to the next, it has to be transformed to an intermediate representation on one side, and back on the other side.

In short: The reference on one node will not point to the same location as the one on the other node. This means that we cannot check for equality (which is stronger than, but would infer, equivalence, which is what your code actually requires) by comparing pointers.

In Erlang/Elixir, all data structures can be checked for structural equivalence by matching on them. However, functions are, for the reason outlined above, opaque, so we cannot check their internals.

And therefore, an extra symbolic intermediate step like what @NobbZ proposes is required.


In a more general sense, you can never prove the equivalence of two functions, because this would be the same as solving the Halting Problem. The only thing you could do is to prove that two functions are equivalent because they are equal (exactly the same instance), but this is something that is not possible in Elixir because of above-outlined restrictions imposed by the distributed nature of Elixir’s runtime system.

Also Liked

benwilson512

benwilson512

Author of Craft GraphQL APIs in Elixir with Absinthe

https://happi.github.io/theBeamBook/ is a good resource for looking at the internals of how the BEAM itself works. Decisions and capabilities like the one you’re describing are gonna either be present or absent on the basis of the BEAM itself, Elixir just gets compiled to BEAM byte code.

Qqwy

Qqwy

TypeCheck Core Team

@jgonet There are not many cases in which you actually touch the Erlang/BEAM low-level stuff directly or have to keep them in mind (most of the time, Elixir’s abstractions are very leak-free). However, I would recommend watching Solid Ground because it will give you a good high-level foundation of understanding what trade-offs have been made in Erlang/BEAM :slight_smile:.

NobbZ

NobbZ

There are no function pointers in elixir.

&+/2 is different in every module you write this. Also, even though both of the following functions are obviously producing the same result for their respective domains, you can’t computationally compare them for equality without doing manual prooves:

def multiply_a(a, b), do: a * b

def multiply_b(0, _), do: 0
def multiply_b(_, 0), do: 0
def multiply_b(1, b), do: b
def multiply_b(a, b) when a > 0, do: b + multiply(a - 1, b)
def multiply_b(a, b) when a < 0, do: multiply(-a, -b)

Both of these functions are “equal” to */2, but for the latter it will be very hard to proof in a language like elixir.

Just rewrite your dispatcher to use atoms (or accept all functions with arity 2).

def f(op, [a, b | s]) when op in [:+, :-, :*, :/], do: [apply(:erlang, op, [a, b]) | s]
def f(v, s) when is_number(v), do: [v | s]

def g(op, [a, b | s]) when is_function(op, 2), do: [op.(a, b) | s]
def g(v, s), do: [v | s]

Where Next?

Popular in Questions Top

JDanielMartinez
Hi! May someone helps me, please! I have two apps into an umbrella project: the first one is Database, which manages queries, and the se...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
vac
Hi, I'm quite new in Elixir and I'm trying to format a string to a PEM format. I have the certificate value like MIIDBTCCAe2...... and ...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
Exadra37
Sometimes I want to check if the input into a function is not a blank string. My first approach: defmodule Example do def do_stuff(s...
New
hpopp
To simplify some tasks at work, I wrote and published this package yesterday. It’s a simple macro that enables Access behaviour on struct...
New

Other popular topics Top

sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
977 41022 311
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
albydarned
Hello all! I am typing this post from my new MacBook Pro with the M1 chip. I’m loving it so far, and will probably use it as my daily dr...
New
openscript
Hello! Sorry for this astonishing simple question, but I’m really stuck. I try to set up the intellij-elixir plugin, but I don’t know ho...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
polypush135
As many of you may have realized by now (sorry for all the posts here) I’ve been working on a db problem where I’m trying to aggregate a ...
New
shahryarjb
Hello, I have map which I want to convert it to string like this: the map: %{last_name: "tavakkoli", name: "shahryar"} the string I ne...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New

We're in Beta

About us Mission Statement