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

I disagree from this part:

"I also think it's partially wrong. No one in the history of the world has ever been confused or upset by a + b calling a function."

It depends. If this is simple math on vectors I think it can be OK but it should probably be a built-in feature of the language as this is common, solved and we all implement it the same way (for short vectors at least)

But the + operator has been abused in the past, especially with strings concatenation and I think this is a huge liability. Such an innocent looking operator, the simplest of all operations, leading to a function call, a memory allocation and thus a very real potential memory leak, all of that hidden from the eyes...

In my opinion, clarity should have priority over anything else. If an operation is computationally complex (especially with side effects) it should be at least hinted to the reader by a function call.



I also would prefer not to have + as concatenate, and Linus feels strongly enough about it (and has enough influence) that Rust for the Linux kernel does not have Add and AddAssign overloaded on string types, among many concessions.

However I think in general purpose programming we lost that battle when Java special-cased the operator. There's no overloading in Java, you can't have + do the Right Thing™ in Java for your 3D vector class, but it does concatenate strings because people had begun to expect that.

Also, while I'm in this topic, the author argues they don't want anybody overloading either of the member access operators (which is something Python kinda-sorta supports) but notice that Rust effectively does this all the time and nobody freaks out because it feels completely natural.

Your Box<Thing> isn't a Thing, so, why can you call Thing methods on it? Because it is transparently passing those into the underlying Thing by implementing the relevant overload features from core::ops and all of Rust's smart pointers work the same way.

misfortunate::Double shows that this gives very strange behaviour if somebody uses it inappropriately, a Double<Thing> is actually two Things inside, but when you change it, you're changing one of them, while any immutable references are to the other one... an eerie experience.


Why Java given the history of programming languages between 1950 and 1996?


Influence. A completely amazing amount of Java was written (is being written) and there's a huge workforce in that language.

I think that it's possible if say Gosling hated + concatenating and had provided Java with a different String concatenate (it clearly wants a concatenate operator, but it needn't be named +) we'd see that popularised and while some languages with overloading might overload + it wouldn't be ubiquitous. I can't prove that of course, it's purely my opinion.


What? Influence, what a nonsense.

Even BASIC uses it, and it was everywhere during the 15 years that preceded Java.

Let alone all the other languages since Jovial that can't be bothered to dig out just to prove my point.


Huh. I'm pretty sure that at least some BASIC dialects I used did not have concatenation, but you seem to be correct that in general they did. This intrigued me enough to go dig through nested "from old machine" directories until I found some BASIC (I'm going to guess that I have not written any BASIC since about 1992) and you're correct that BASIC from the era which didn't live on cassette tape and thus I still own does have concatenating + operators in it.

I stand corrected.


> However I think in general purpose programming we lost that battle when Java special-cased the operator.

It was already special-cased by Pascal.


> Such an innocent looking operator, the simplest of all operations, leading to a function call, a memory allocation and thus a very real potential memory leak

I hadn't considered the memory leak angle of this, and now that you mention it it's clearly a driving concern here. When freeing is explicit, it's a problem to heap-allocate temporaries. (And I guess the addition operator would also require a third operand to supply the allocator?)

I'm still a big fan of destructors, and how they make this problem mostly vanish. But I understand that it's hard to make them efficient without move semantics, and maybe also a global allocator so every object doesn't need to store an allocator pointer? What other interactions am I missing?


Strings are complex entities, I am not sure if there is a perfect way to handle them.

In my case (I use my own framework) I use a lot of makeStringWith* functions that all allocate in the same global scratch buffer (one per thread) and I don't care at all about releasing the memory.

There is simply one function to clear reset all scratch buffers, it has to be called explicitely by the user, most of the time once per frame (I create video games) but it can also be when the current job is done or never.

From my perspective it is very efficient and reliable, never had to solve a bug related to this mechanism.


At the very least, it's wrong that no one has ever been upset by this; many people have been very vocally upset by this.


> Such an innocent looking operator, the simplest of all operations, leading to a function call, a memory allocation and thus a very real potential memory leak, all of that hidden from the eyes...

It's not a given that floating-point addition is a simpler operation than string concatenation.

If you're serious about what you write, then all operators should be banned.


What is a case where floating-point addition is more complicated than a string concatenation? Are you referring to some obscure architecture?


One example would be an architecture on which hardware floating point is not implemented, and has to be emulated in software. This isn't uncommon in embedded, many ARM Cortex-M cores are like this AFAIK.


When I was writing arm26 (i.e. arm2) assembly for the Archimedes everybody passed around a block of ASM that did division since the processor had a whole 16 instructions and integer division wasn't one of them.

Things are less primitive these days but the memory still brings a smile to my face.


MIPS was also like that, if I remember correctly, division and multiplication were assembler macros.


Reading about MIPS was where I first encountered the concept of a branch delay slot and I am -so- glad I've never had to write assembler for a processor with that feature.


There's a pretty big difference between doing dynamic memory allocation for string concatenation on ALL systems, and doing software addition on some embedded processors. Even on your embedded system, floating point addition is simpler than string concatenation.


I don't know, I'd argue that in the simplest cases—a bump allocator followed by a memcpy—string concatenation can be significantly simpler.


In the worst case, and this is pretty rare these days, yes, adding two floats is a function call, but without memory allocation.

Integer division or multiplication of long (64bits) can also force the compiler to inline a bit more code or even to call a function on older architecture, hardware division was not a given on ARM before ARMv6 I think, so for example on the Gameboy Advance or even the Nintendo DSi you had to be careful with division etc.

But again, this will only slow down your code if you're not careful, not generating memory leaks silently in the background.


Why 'memory leaks'? Presumably, destructors will be automatically called one way or another.

In the simplest case, you can just store all strings on the stack.


The DSL I'm currently hacking around on has ++ for string (and list) concatenation and using a -seperate- operator seems to rather help.

(I've yet to conclude if stealing ++ for this rather than preinc/postinc was a terrible mistake, so far in context it hasn't seemed to be but I'm still keeping an eye on the question)


Perl uses ~ for string concatenation.

Nothing says that you cannot use both infix "++" for concatenation and unary ++ for increment. "-" is both a unary prefix and binary infix operator, for example.


Perl uses '.' for string concatenation.

Nothing says that I can't do that but in context of deliberately -not- re-using operators for completely different things it would seem rather self-defeating.


Oof, you're right. I was thinking of Raku.


Interestingly since you can't pass an allocator to an operator call, there is some extra guarantee around whether a hypothetical overloaded operator can allocate. If you implement it for a vector type which doesn't contain an allocator reference, then you're sure that any operators won't allocate.




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: