Qqwy

Qqwy

TypeCheck Core Team

A possibility for extending the usability guard clauses

In Guard clauses (the when... part after the destructuring section of a pattern match), we’re only allowed to use a very small subset of functions because these are the only ones that the BEAM can optimize and that are known to be pure (side-effect free).

One of the problems of this property of the BEAM, is that it becomes impossible to ‘nest’ guard clauses. In Elixir, we can write macros, allowing us to write short snippets in a guard clause that at compile-time expand to longer pieces of guard-safe functions. But great care needs to be taken to ensure that this macro does not include code that itself tries to pattern match or run guard clauses (or any other type of conditional branching for that matter) because the compiler does not allow this.

In Elixir, we have structs, which are a great type of abstraction to allow many different kinds of user-defined types and work with protocols on them. However, we cannot check properties of a struct inside a guard clause, because it is impossible to destructure a map inside a guard clause (there are no guard-safe functions that can do this).

However, it is possible to bind names to fields of your struct inside the destructuring part of the pattern match, and refer to these names inside the guard clause. But of course, this would mean hard-coding the pattern match every time I want to check if some property of a struct is true.

I think that it is possible to build an abstraction layer that allows this, though. After all, aren’t:

def foo(tuple) when elem(tuple, 0) == :ok do
  # ...
end

and

def foo({42, _} = tuple) do
  # ...
end

the same?

Therefore, why not write something like this:

  def foo(%Box{}) when is_filled(box) do
    # ...
  end

and enhance the Elixir compiler so it will cleverly rewrite it to:

  def foo(%Box{value: tmp0}) when tmp0 != nil do
    # ...
  end

?

I think that by doing this, we would be able to create a lot more possibilities for creating guard-safe macros. I believe that things like the arithmetic operators might finally be able to be made guard-safe as they could dispatch on structs then.

The only downside I can think of, is that it will make compilation a little more difficult.

What do you think? Interesting? Awesome? Horrible?

Most Liked

OvermindDL1

OvermindDL1

It’d not be ‘that’ hard to do all things considered. The elixir parser would just need to be made aware of matching contexts and call the macros over the whole context instead of just the part it is defined in, I did an experiment with this with my (highly incomplete, do not have time to work on it currently, busy at work) defguard experimentation:

# Defining
defmodule StructEx do
  import Defguard
  defguard is_struct(%{__struct__: struct_name}) when is_atom(struct_name)
  defguard is_struct(%{__struct__: struct_name}, substruct_name) when is_atom(struct_name) and struct_name === substruct_name
  defguard is_exception(%{__struct__: struct_name, __exception__: true}) when is_atom(struct_name)
end


# Using
defmodule Testering do
  use Defguard
  import StructEx

  def blah(any_struct) when is_struct(any_struct), do: 2
  def blah(_), do: 0

  def blorp(exc) when is_exception(exc), do: "exceptioned"
  def blorp(val), do: "No-exception:  #{inspect val}"
end


# Testing
assert Testering.blah(%{__struct__: Blorp}) === 2
assert Testering.blah(42) === 0
assert Testering.blorp(%ArithmeticError{}) === "exceptioned"
assert Testering.blorp(%{__struct__: Blah}) === "No-exception:  %{__struct__: Blah}"

My style here is of course a huge hack, it could be done properly in the compiler itself and more cleanly.

EDIT: Your box example (I think) could be done in my library as-is though (I’ve barely implemented it, just enough for the above examples to work, but this might work):

defmodule Box do
  import Defguard
  defguard is_filled(%Box{value: v}) when v != nil
end

...
  def foo(box) when is_filled(box) do
  end

Where Next?

Popular in Discussions Top

andre1sk
A big advantage to Elixir is all the distributed goodness but for many applications running on multiple nodes having integrated Etcd, Zoo...
New
jeramyRR
This is an interesting article to read. Elixir’s performance, like usual, is excellent. However, it seems like the high CPU usage is co...
New
JakeBecker
TL;DR: I’ve just released an implementation of Microsoft’s IDE-independent Language Server Protocol for Elixir. It adds language support ...
1144 53690 245
New
lucaong
Hello Elixir and Nerves community, I have been working for a while on an open-source embedded key-value database for Elixir, that I call...
230 13924 124
New
AstonJ
If a newbie asked you about Phoenix Contexts, how would you explain the basics to them? Feel free to be as concise or in-depth as you li...
New
shishini
I think this twitter post and youtube video didn’t get as much attention as I hoped I am still new to Elixir, so can’t really judge ...
New
chulkilee
Here are the list of HTTP client libraries/wrappers, and some thoughts on HTTP client in general. I’d like to hear from others how they w...
New
acrolink
How does the two languages compare when it comes to server side application development? Any experiences or ideas? Thank you.
New
AstonJ
If so I (and hopefully others!) might have some tips for you :slight_smile: But first, please say which area you’re finding most challen...
New
slashdotdash
Phoenix Live View is now publicly available on GitHub. Here’s Chris McCord’s tweet announcing making it public.
New

Other popular topics Top

marius95
Hello everyone, I try to use an Javascript Event Handler in my root.html.leex file. Therefore I created a function in the app.js file: ...
New
sorentwo
Hello! tl;dr Announcing Oban, an Ecto based job processing library with a focus on reliability and historical observability. After spen...
985 42920 311
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
Fl4m3Ph03n1x
About me? ( if you have nothing better to do than reading about some random guy in the internet :stuck_out_tongue: ) Hello all, this is ...
New
jerry
Good day to you all. I have been struggling to get a query involving like and ilike to work. Can anyone assist me on this, please? pro...
New
jaysoifer
Is there a way to rollback a specific migration and only that one (“skipping” all the other ones)? Would mix ecto.rollback -v 200809061...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
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 52341 488
New
klo
Got a question about when to concat vs. prepending items to list then reversing to achieve appending. So i know lists boil down to [1 | ...
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement