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

quinta-feira, 21 de Março de 2019 às 17:31:36 UTC, Chris Russo escreveu:
>
> 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.
>
> On Monday, March 18, 2019 at 12:38:01 PM UTC-4, Mário Guimarães wrote:
>>
>> 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
>>
>>

-- 
You received this message because you are subscribed to the Google Groups 
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to elixir-lang-core+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/elixir-lang-core/74865852-84cc-4c16-8e1a-3ea8e3488a3f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to