[Proposal] defnotoverridable

60 views
Skip to first unread message

Mário Guimarães

unread,
Mar 18, 2019, 12:38:01 PM3/18/19
to elixir-lang-core
Hello,

I would like to propose the addition of a new feature to Elixir.

Suppose a team created the following module

defmodule A do
  defmacro __using__
(_opts) do
    quote
do
     
def task(:coffee), do: "make coffee"
     
def task(:cookies), do: "make cookies"
   
end
 
end
end

Now, a developer in some other team creates the following module

defmodule MyModule do
 
use A
end

This module allows to make coffe or cookies, and this is a good thing.

Now suppose that after some time, someone else makes finds the next module useful

defmodule B do
  defmacro nice_macro
() do
    quote
do
     
def task(x), do: "something good is done, but sometimes there are hidden missiles being launched"
   
end
 
end
end

and modifies `MyModule` like this

import B

defmodule
MyModule do
 
use A
  B
.nice_macro()
end

Oops!!! Very bad things can happen now if `MyModule.bar/1` is called with anything but `:coffee` or `:cookies`.

This situation can be called an "unexpected clause problem" because it makes an existing
function to be inadvertently redefined by code defined sometime and somewhere else.

The proposed solution to this problem is to introduce the `defnotoverridable` clause, like this

defmodule A do
  defmacro __using__
(_opts) do
    quote
do
     
def task(:coffee), do: "make coffee"
     
def task(:cookies), do: "make cookies"
      defnotoverridable
[task: 1]
   
end
 
end
end

or like this, by declaring all existing functions at some point to be not overridable

defmodule A do
  defmacro __using__
(_opts) do
    quote
do
     
def task(:coffee), do: "make coffee"
     
def task(:cookies), do: "make cookies"
      defnotoverridable
:all
   
end
 
end
end

Note that a `defnotoverridable :all` can also be a strong guarantee against a missing `defnotoverridable`, like by doing

defmodule B do
  defmacro __using__
(_opts) do
    quote
do
      defnotoverridable
:all # defend just in case task/1 was previously defined at the macro call site
     
def task(x), do: "something good is done, but sometimes there are hidden missiles being launched"
   
end
 
end
end

Furthermore:

1) a `defnotoverridable` cannot be undone by a subsequent `defoverridable`, but a `defoverridable` can be undone by a subsequent `defnotoverridable` (the point is, `defoverridable` indicates a possibility, whereas `defnotoverridable` indicates an impossibility)

2) a `defnotoverridable [foo: 1]` requires function `foo/1` to have been defined before

I hope you find this addition useful.

Thanks,
Mário Guimarães

Chris Russo

unread,
Mar 21, 2019, 1:31:36 PM3/21/19
to elixir-lang-core
Once you start down the road of trying to secure included libraries, you take on a huge responsibility that entails a great deal more low-level language work.

Mário Guimarães

unread,
Mar 21, 2019, 2:58:54 PM3/21/19
to elixir-lang-core
Hi Chris,

I think if we follow that reasoning then we won't do anything. Do you know of other problematic issues that you want to share? Perhaps in other topics.

This problem I wrote about is one that exists, and that I believe it deserves some thought.

The important thing is to warn the developer when two separated `quote do ... end` blocks inject clauses of the same function/arity.

Instead of `defnotoverridable`, a better solution, after discussing with Eric Meadows-Jönsson, could be to do something like

defopen [task: 1]

after the last clause for task/1 in the first quote block.

More generally, the idea is for the compiler to warn when another clause for task/1 is introduced by a different context from that which introduced it first, in case the first context did not ended with a `defopen [task: 1]`; otherwise if it did so, then the compiler does not emit the warning for task/1.

I don't know the internals to understand if this is something hard or even feasible to do, but at least, both the problem and a potential solution have been documented.

Thanks
Mário Guimarães
Reply all
Reply to author
Forward
0 new messages