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

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
sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
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
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: https://hexdocs.pm/ecto/Ecto.Schema.html#module-...
New
ycv005
I have followed this StackOverflow post to install the specific version of Erlang. And When I am running mix ecto.setup then getting fol...
New
dblack
I’ve got an issue with an app and I’ve no idea of how to troubleshoot it. I’m hoping someone here might have seen something similar. I p...
New
nsuchy
Hi. I’ve noticed that Windows Powershell has it’s own IEX command and you cannot access Elixir’s IEX due to the conflict. This isn’t a cr...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
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
hariharasudhan94
Lets say i have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; "XX...
New

Other popular topics Top

sen
Hi All, I set a environment variables in dev.exs , like below code. when i start server, how can i set the ${enable} value? thanks. d...
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
Nvim
Anybody knows a comprehensive comparison of Django and Phoenix, thanks for the help. Where are they similar? Where do they differ the m...
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
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
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52238 488
New
chrismccord
This release brings a number of exciting features, including integration with the new Phoenix LiveDashboard and Phoenix LiveView. There h...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
PeterCarter
There are pre-rolled solutions for other frameworks that do work. However, Phoenix does not seem to have these. Have people had good expe...
New
dogweather
I wrote this comment on r/haskell, and it’s not popular there. :wink: But I think I’m on to something… Haskell reminds me of Java, and e...
New

We're in Beta

About us Mission Statement