Fallback to nearest prior imported library module and not necessarily Kernel when overriding an operator?

Nope. Sounds like I am just challenging certain predetermined aspects of the current version of the language. So?

Where did I mentioned 3 or 4 arguments? I just wanted a little more freedom in overriding the operators. And if those special forms are implemented as they are now (in theory) it doesn’t mean they need to stay that way. It’s a software for God’s sake. Nothing is written in stone.

What do you mean by weird behavior? “If you’re using another library that overrides the operator X, just use this one after the import/use of the former and it will all work.” Is that a weird, hard to maintain behavior?
The only scenario in which my approach would not work is if the other library matched on the very same value patterns that my operator override matches and this would be documented. In your case, it would take turning one off to turn the other on even if there’s never such a case, only to accommodate the very fact that you’re overloading the same operator, while mine still leaves the same option to the user without mandating it to them for the 99.9999% of the cases => If my use just imports what it imports, then the user is also free to import it manually the way you suggest, but if I don’t make what I said I would, then it becomes the only option in spite of an ultra miniscule and still warned about chance of a conflict.

Yes, I agree with that. Part of the reason I went from OO to all-in functional.

But I have another solution and that is to write my own import (say import_plus) that reuses import in full but adds the :override option, that will do all the magic that I mentioned so the user does not need to this on their own or use the library.

Sorry, but I do not see this good. In same way someone may challenging Ruby-like syntax (do … end notation) or functional programming and what the community should reply for such a message? Well … you are free to use other languages or write your own?

Ok, so … you are using naming without understanding what it means? As before non-unary operators have 2 arity. different arity are your words. Now see the definition of arity. As in wikipedia the arity is number of arguments, but somehow you changed it to freedom in overriding. What’s more interesting you have this freedom as you are overriding operator, but instead you have conflict between two libraries. :exploding_head:

In fact it is and this is terribly important in programming. Before next major version (2.x) of Elixir we can expect new features and not changing behaviour of current ones. If that would not be a case upgrading between every Elixir release would be like a hell as every time you would need to update all of your projects (from pet project up to production ones) in order to make them work in new version.

There is even no schedule plans for 2.x version of Elixir and to replace language basics you would need to convince not just maintainers, but also the whole community. So far you have no likes or replies like I also have such problem and therefore for now it looks like that would be a change for just one person for just one use case in which you have other ways to solve conflict problem.

It’s not like that community does not consider changes in language, but just for example say … When last time you have used static types in Elixir? Even if community is willing to consider adding this the topic have many years. Every change of behaviour would affect whole community and not only you. It’s not likely such changes would be delivered quickly if any.

Our community is rather used that some library could override something from Kernel (like def in components), but I do not remember a case when one library changed other one. Of course there are libraries that extends other libraries in many ways, but they are not overriding their functions/macros. For you it’s a topic like anyone else, but for others it’s rather something new. If it would not be new your posts would be merged with existing topic or this topic would be closed mentioning that there is a similar one or somebody would link to existing solutions working already in production.

Never heard about such case. It’s rather use B instead of A and not override A to make B work. If you have shared code simply use it in both A and B and let them define their own function/macros. I completely do not see a use case for what you are looking for.

defmodule Shared do
  defmacro __using__(_opts \\ []) do
    quote do
      def shared, do: :ok
    end
  end
end

defmodule A do
  defmacro __using__(_opts \\ []) do
    quote do
      use Shared
      def sample, do: :a
    end
  end
end

defmodule B do
  defmacro __using__(_opts \\ []) do
    quote do
      use Shared
      def sample, do: :b
    end
  end
end

defmodule Example1 do
  use A

  def run do
    IO.inspect(shared())
    IO.inspect(sample())
  end
end

defmodule Example2 do
  use B

  def run do
    IO.inspect(shared())
    IO.inspect(sample())
  end
end

Example1.run()
# :ok
# :a
Example2.run()
# :ok
# :b

As before import is not a macro like those you write. It’s expanded by compiler. Do you really expect to rewrite compiler for just one case?

Think that everyone would do like you. Every library would need to maintain their own compiler in fact it would be like every library would have it’s own variation (slang?) of language. This is just a crazy idea. Alternatively every library request a change to Elixir language… It would become a dumpster.

This is why by default developer should not think how I could change the language to make my code work especially if you have alternative ways to make your library work.

1 Like

There’s a simple solution to that imo:

OtherLibrary.*(2, %{a: 1})
MyLibrary.*(2, [a: 1])
Kernel.*(2, 2)

No guesswork involved. No fallback logic needed. Each function can properly error if their inputs are incorrect. Yes you’d not use the syntax sugar you usually get with operators, but to me that’s a worthwhile tradeoff for the benefits.

But I’d imagine you’re still looking for that syntax sugar in the solution:

def main(multiplier) do
  mult_map(multiplier)
  mult_keyword(multiplier)
  multiplier * 2
end

defmap mult_map(multiplier) do
  multiplier * %{a: 1}
end

defkeyword mult_keyword(multiplier) do
  multiplier * %{a: 1}
end

This would mimic the defn approach you see in Nx. That one allows putting elixir code in a completely separate context (tensor calculations in the case of Nx), but importantly it puts it in just a single known context. Again no guesswork or surprises, no fallback logic, but still the ability to write elixir code with normal operator syntax and still have things work.

I wouldn’t suggest that as the default answer to overlapping operator overloading/overwriting of libraries, but it’s an option where extended code needs to work with overloaded operators without juggling imports.

I think I’ve heard this argument before, but it was about making one’s own Twitter. Can’t say it aged well for the proponents.

How else do you think a technology evolves? A genuine question.

Yes, different arity in terms of 1 instead of 2. I already gave you the examples.

That was a digression. If in doubt, feel free to re-read the entire exchange above. And I do not have any conflict between two libraries. In face, the whole discussion was initiated because I didn’t want to have one in the future.

Tell me about it. Been with LiveView since its v0.4 :slight_smile: PS. just so you don’t get into formalities again, I am aware that 0.x releases are usually volatile with plenty of breaking changes and that a major release should hold on to its interfaces (for the purity of expression sake, may I say interfaces or you’ll find it formally unacceptable again because words have meanings and there are no formal interface constructs in Elixir? )

I don’t need to. That’s why I can write my own libraries.

IMO Elixir does not need a static typing.

Ever hear of precedents being drivers of innovation/change)?

See the Mark Twain’s quote at the end.

So?

I got that.

My view is that every single piece of open source technology is a foundation for a market of ideas. It is not what is academically correct that should prevail but what is proven to be most useful over time. I love Elixir as it is now, but there are things to consider improving. Not saying that my ideas should necessarily be the ones that are accepted or approved. Maybe the ideas of others address a bigger scope and thus solve my problems automatically. Only time will tell.

Lastly, a word of advice to you, buddy, in three bullet points:

  • No hard feelings
  • Whenever you find yourself on the side of the majority, it is time to reform, pause or reflect. — Mark Twain
  • Chill man, chill.

I appreciate your opinion (here and in general). But with what you just said in mind:

  1. How do you defend the position of what is typically done in libraries (or has been done up until now) when it comes to the fallback to Kernel? How come this “fallback depth of 1” is acceptable while a “fallback depth of n where n >= 1” is not? An additional cost cannot be an argument, for it only occurs in a very tiny percentage of cases while the library would still mostly fallback to the Kernel implementation, but at least wouldn’t be unusable in that same tiny percentage of cases. Please bear in mind that I am talking about a very generic application that is more likely than not going to create issues (litter the code with switching the thing on/off) when used together with another hypothetical library with the same operator override in that miniscule number of cases.

  2. You’re right about the syntactic sugar. To me, personally, this minimalistic syntactic sugar style is one of the reasons why I like Elixir so much. And because I like it so much, I want to be able to expand on it myself, not just use the existing ones. The libraries I write now are all about that. They do not introduce new ways of computing things - they make things simpler to write and easier to read (assuming the acceptance of the localized syntax extension).

Btw, the problem with your second approach is that in my case it is not applicable. The reason why I am using operators (and not standard macros) in the first place is to take precedence over the evaluation of the arguments. If there was a way to do this with standard macros, I would most likely use them instead.

Can you expand on that? I personally don’t advocate for any fallback to Kernel. anywhere. You might be confusing this with Kernel implicitly being imported, but that’s (at least technically) not a fallback. You still either have the operator be defined by Kernel or you have it defined by whatever else you import.

Most libraries do something like this:

def doit( whateverpattern) do
 # library behavior
end

def doit( other) do
  <oprerator> other
  # or
  #Kernel.<operator> other
end

That’s what I refer to “fallback depth of 1”.

For that I think my argument applies as well. Fallbacks hide boundries and make input validation tricky. It’s imo just complexity, which makes understanding code more difficult. That’s also a point for your previous comment. There’s a difference between easy to read and easy to understand. Imo the latter is more important than the former. It might seem nice to just be able to multiply whatever, but it’ll be much more complex if it doesn’t work as expected and you need to get an understanding of why that’s the case if you’re dealing with a matroschka doll of overloads/fallbacks.

That’s also very close to the composition over inheritance argument btw.

Agreed on difference and agreed on understanding. However, I find understandable more subjective than readable so, personally, I tend to lean towards to prioritizing readable.

Clear. The accountability assumed here :slight_smile:

Also true.