Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

It wasn't a mistake, just a deliberate compromise.

My goal was a very small, simple, but flexible language. It needed to be pretty tiny to be embeddable in lots of applications. (And pragmatically, so that I could design and build the whole language myself.)

Single dispatch works really well for, like 90% of the kinds of operations you want to perform in programs. It reads really well syntactically and avoids having to namespace every function with its argument type like you see in C and Scheme (hash-remove!, dict-remove!, set-remove!, etc.). And it does this without needing types or static resolution. Also, it's simple to compile to something fairly efficient at runtime using a vtable-like structure.

It just sucks for binary operators. But coming up with a better solution for that requires something like:

* Static types and static dispatch like C++, C#, etc. do.

* Multimethods and multiple dispatch.

* Double dispatch like Python.

* Hacking something special into the language for operators like not allowing them to be overloaded and just having a fixed set of types they apply to.

Static types and multimethods are a huge jump in complexity. Python's approach is really slow. Special casing operators makes me sad.

So I just took a relatively small expressiveness hit and decided that, yes, operators aren't symmetric. In practice, it's mostly fine.



Sorry for focusing on such a small part of your comment. I’m learning about language design (as much as I can) and I don’t really understand what you mean by “double dispatch like Python”.

I think (maybe soon thought) that Python has single dispatch. Since you’ve invented languages and work on them I’m pretty much 100% sure I’m wrong and would love to learn why.

I read https://en.m.wikipedia.org/wiki/Multiple_dispatch and came to the conclusion Python has “single dispatch polymorphism” because the method resolution is based on the type of the calling object dynamically at runtime and there is no method signature overloading, which means the argument type(s) doesn’t play a part in picking/resolving the method to be called.

If you have time, do you mind explaining or pointing me to some resources?


I think he meant that for Python operators, the method can be dispatched based on the first or the second argument.


Yes, this is a special case that Python implements for binary operators. When evaluating "a + b", if a.__add__(b) doesn't work, it'll try b.__radd__(a). See https://docs.python.org/3/reference/datamodel.html#object.__...


Yeah - `__add__` then `__radd__` isn't exactly double dispatch, but it's close. (There are corner cases involving inheritance where double-dispatch will work correctly but Python's approach will fail to pick the most-specific method.)


Yes, this is what I was referring to.


It is a mistake, because if you can't do "1.0 - z" or "0.5 * A", I don't know why you have operator overloading at all. What types with useful binary operators don't need to interact with scalars? Sets? Sequences? There aren't many math-y things that don't need numbers. The absence of complex values and matrices makes me sad, and I can list a dozen more on top of those.

Yes, static typing would be a huge change, but I think you're greatly exaggerating the complexity of multimethods for (only) binary operators. You don't want to do it, which is fine. It's your language, and you don't owe anyone anything, but be honest about your rationale.

You've already got a failure path to report errors when the types don't work, so falling back to a single global table of "first_type x second_type => function" is not more "huge" than anything else. You don't need scoping for operators like this, so it's literally one table. You can see the code in your head as you read this. I'm sure you could fill in the details or imagine some other simple implementation if you wanted to.


> I don't know why you have operator overloading at all.

Wren doesn't have "operator overloading". There is no separate feature in addition to the normal operator support that also lets you overload them. Wren just uses single dispatch for infix operators. Even the "built in" operator behavior like addition on numbers is simply pre-defined methods on the Number class. Like Smalltalk (which Wren is heavily inspired by), everything is just a method call and there is little "special" behavior for primitives.


So if I understand this correctly, "a + b" is syntactic sugar for "a.+(b)" (modulo the exact name of the method)?


Yes, exactly. Basically everything is method calls.


"It wasn't a mistake, just a deliberate compromise."

This might describe 80% of all the disagreements people have.


simplicity is definitely valuable, but it's not just operators where this type of thing shows up.


That's why I said "90% of operations" :)




Consider applying for YC's Fall 2026 batch! Applications are open till July 27.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: