Function/Method look up in Elixir/Ruby
Tags: elixir, ruby, beginners, functional
Published: 2019-03-21 10:31:17

So to preface, I'm a Ruby dev learning Elixir. I love to compare what I'm learning in Elixir back to what I know in Ruby, as I think that really strengthens my understanding of both languages. Today, I want to talk about function/method look up.

This article was prompted when I was learning about [`GenServer`](https://hexdocs.pm/elixir/GenServer.html) through [PragmaticStudio's course](https://pragmaticstudio.com/courses/elixir) (I highly recommend it), and they mentioned that `GenServer` can inject default implementations of functions if you do not define them manually. These default functions are:

- `init/1`
- `child_spec/1`
- `code_change/3`
- `terminate/2`
- `handle_info/2`
- `handle_cast/2`
- `handle_call/3`

And if you want to override them, just define a function in the current module with the same signature (name + arity).

But this got me thinking, *how* is it that Elixir look up function definition that makes this possible?

Before we jump into Elixir, let's first look at how method lookup in Ruby works.

## Your dad, your dad's dad, and your dad's dad's dad.

I have actually blogged about this before, which was [retweeted by Matz](https://twitter.com/yukihiro_matz/status/1069401210162016256) himself! (I actually have no idea how much this actually means, but I'm still going to wear it like a badge of honor)

{% link https://dev.to/edisonywh/ruby-has-no-class-methods-39l5 %}

Ruby's method lookup chain can literally be summarized in one method call - `ancestors`.

```ruby
Foo = Class.new
Foo.ancestors # Lookup path for instance methods
#=> [Foo, Object, Kernel, BasicObject]

Foo.class.ancestors # Lookup path for class (singleton) methods, read my blog to learn more!
#=> [Class, Module, Object, Kernel, BasicObject] 
```

That's it -- that's how you see the entire method lookup chain for Ruby!

In fact, `#ancestors` is defined on `BasicObject` itself, so when you call `Foo.ancestors`, it asks this:

```diff
You: Do you implement `#ancestors`?

- Foo: No
- Object: No
- Kernel: No
+ BasicObject: Yes I do!
```

And that's how it works! 

With that, we know that if we want to `inject` a function into Ruby, we just have to make sure that the newly injected function is the first method that gets found during the lookup (defining `#ancestors` anywhere before `BasicObject`).

So, Ruby is able to do this because of `ancestors`, but Elixir doesn't really have the concept of `ancestors`/`inheritance`, so what is really happening here?

It's about time to stop guessing, so I decided to try out an example myself.

```elixir
defmodule Parent do
  defmacro __using__(_opts) do
    quote do
      def injected do
        "This is from Parent"
      end
    end
  end
end

defmodule Child do
  use Parent

  def injected do
    "This is from Child"
  end
end
```

My expected behavior is that my `Parent.injected/0` will be overriden by my `Child.injected/0`, but when I try to run this file - `iex lookup.exs`, the following warning message pops up:

> warning: this clause cannot match because a previous clause at line **12** always matches lookup.exs:**14**

Weird warning, but let's try to actually run it.

```elixir
iex(1)> Child.injected
"This is from Parent"
```

To my surprise (well the surprise is kinda ruined when I saw the warning message), running `Child.injected` returns me `Parent.injected/0` instead of `Child.injected/0`, why?!

I decided to [dig into the doc](https://hexdocs.pm/elixir/GenServer.html) and find out why, but the docs isn't really tell me much apart from the fact that `GenServer` *will* inject functions for you. But I already knew that, I just want to know *how* is it doing so.

So, docs didn't help, what next?

## Source Code!

As like most other languages, Elixir is open source, so I could very easily dive into the [source code itself](https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/gen_server.ex).

Now I see something [suspiciously named](https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/gen_server.ex#L818) `defoverridable`, and I have a hunch that this might be it, but I want further proof to my hypothesis.

A quick Google about `defoverridable` sent me to a post in [Elixir Forum](https://elixirforum.com/t/behaviours-defoverridable-and-implementations/3338), written by the man himself, José Valim.

## Solution

So through that post I found out that I can use `defoverridable`, which would mean that I can update my code to the following:

```diff
defmodule Parent do
  defmacro __using__(_opts) do
    quote do
      def injected do
        "This is from Parent"
      end

+     defoverridable injected: 0
    end
  end
end

defmodule Child do
  use Parent

  def injected do
    "This is from Child"
  end
end
```

And now if we run it..

```elixir
iex(1)> Child.injected
"This is from Child"
```

Voilà it works! Perfect :tada:

## Conclusion

In his post, José clarified `defoverridable` for me, but he also went on to say that `defoverridable` is **not recommended**, and they are in fact moving towards to `@optional_callback` to mark methods as optional, along with an enhancement `@impl` (which the compiler will use to make sure "child" implements that function), so that was also an interesting thing that I learned. I recommend you to read the post and comments to understand it a little more.

My query was literally answered in the first 10 lines of his post, so it *would've been really nice* if I managed to find it in the first place. Then again, if I hadn't went into the source code I wouldn't have known what keyword to look for, and thus won't land at that post.

Still, an interesting journey for me because *at one point I thought Elixir had inheritance like Ruby, heh.*

(also, title is technically incorrect as Elixir does not really have "function lookup" - all it does is just look at the current module!)