Qqwy

Qqwy

TypeCheck Core Team

Hello everyone to this week’s edition of ‘metaprogramming with @qqwy’.
This week I was thinking a lot about libraries that override built-in functions or macros.

This is a relative common technique that quite a few libraries use. Two common examples are:

  • Overriding def, defp, or defmodule is common amongst libraries that want to enhance function-definitions in some way.
  • Overriding builtin operators like +/2, -/2or |>/2 to enhance the kinds of data-structures that these operators allow.

However, there is a glaring problem with this technique, and that is that a library containing a definition like this:

def a + b do
  if is_fancy(a) or is_fancy(b) do
    my_custom_logic(a, b)
  else
    Kernel.+(a, b)
  end
end

can never be used together with another library that wants to enhance the same function, macro or operator.

The problem here is that we are blindly falling back to Kernel, meaning that we bypass the other library(ies) that might be in scope.

How can we fix this? Great question!


One idea that I have, is described in this gist. Summarized:

  1. Library-modules that want to override a function or macro contain a ‘default implementation’ that looks up what earlier implementation was in scope and call that one. This default implementation can be automatically injected and annotated with defoverridable allowing a library-implementer to simply call super(...) whenever they want to fall back to the default implementation that is in scope.
  2. Library-modules add a snippet to their own __using__ macro that will hide the conflicting implementations that are in scope which would conflict with their implementations and sets up that this now-hidden implementation is what should be called whenever a fallback is triggered.

In the end we then end up with something that from the user’s perspective looks like this:

defmodule Example do
  use OverrideExample1
  use OverrideExample2
  @a 1
  @b 2
end

Here, both OverrideExample1 and OverrideExample2 have overridden the @ operator macro. Since they use SafeOverride, when the @ macro is called inside Example at compile-time, OverrideExample2’s implementation is used, which will fall back to OverrideExample1’s implementation, which will fall back to the Kernel implementation.


Of course, this is just a single idea.
I’d love to talk about this and hear your opinions! :smiley:

Showing Posts 1 to 2

Ajwah

Ajwah

This is awesome.

I would like to illustrate this challenge with a library that I recently developed: ex_debugger. It hijacks def/defp macros in ones code base in order to annotate the AST at various junctures(start/end of a definition as well as branches in case, if, cond expressions etc.) with debugging expressions that can be toggled on/off.

I would not see how I could leverage any of the two suggested techniques to avoid the problem highlighted in this discussion which in turn reduces the value that a library like mine can offer.

I belief that addressing this issue requires an additional feature in the language and I would love to hear from the community what would make sense. One potential proposal could be to introduce: defhook/defphook(def_heading_ast, def_do_block_ast) that would each return a tuple: {updated_def_heading_ast, updated_def_do_block_ast} and that the standard library effectively uses as input for the actual def/defp-macros. When multiple libraries implement defhook/defphook, then the expected behaviour would be that they queue up so that the output of one hook would constitute input of the next one.

Qqwy

Qqwy OP

TypeCheck Core Team

I am still hoping to hear more opinions on this matter, because I believe that this is an important problem that requires a solution :slight_smile:

— All posts loaded —

Where Next? Top

Trending in Discussions Top

AstonJ
As the title says, please share what you’ve been up to with Elixir. Whether that’s been learning it, looking into it, making stuff with i...
2977 94592 917
New
cblavier
Hey there, It’s been more than a year since we started using LiveView as our main UI library and building a whole library of UI componen...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
heathen
Quite interesting article Google brought me. Didn’t find any mentions about it here. What do you think in general? Would you use togethe...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
New
AstonJ
Since we have deprecated our Erlang sections (as we have dedicated Erlang Forums now) let’s add this thread for those who’d like to post ...
New
Null-logic-0
What IDE or editor are you using for Elixir development? Personally, I use Zed, and I really like it, but sometimes I wish there were a ...
New

Other Trending Topics Top

JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Dmk
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
netoum
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews