News for hackers. Stuff that matters.
Re:C++23: The Next C++ Standard by coldpie on Tuesday July 11, 2023 @02:42PM (#36685449)
Try this on:

    plus(
        mul(vec1, vec2),
        mul(vec3, vec4))
Yeah it's a tiny bit clumsier, and prefix notation takes some getting used to. But on the plus side we avoid all the too-clever travesties programmers have inflicted on us with bad operator overloading decisions! On the whole I think it's easily worth the trade.
Re:C++23: The Next C++ Standard by spacechild1 on Tuesday July 11, 2023 @02:50PM (#36685574)
Again, no thanks. I want mathematical notation and I simply won't use any language without operator overloading. Free functions for common mathematical operations are an abomination.
Re:C++23: The Next C++ Standard by throwaway894345 on Tuesday July 11, 2023 @03:29PM (#36686096)
Then you should probably use a language that lets you write DSLs for any given domain, rather than abusing operator overloading which just happens to work for a few subdomains of mathematics (e.g., you can't use mathematical conventions for dot product multiplication in C++). Anyway, I've never seen any bugs because someone misunderstood what a `mul()` function does, but I've definitely seen bugs because they didn't know that an operator was overloaded (spooky action at a distance vibes).
Re:C++23: The Next C++ Standard by pjmlp on Tuesday July 11, 2023 @04:31PM (#36686835)
Assuming mul does actually a multiplication.
Re:C++23: The Next C++ Standard by throwaway894345 on Wednesday July 12, 2023 @08:11AM (#36693378)
It doesn’t matter, you can look it up easily because without overloading, you know exactly which implementation will run.
Re:C++23: The Next C++ Standard by pjmlp on Wednesday July 12, 2023 @10:10AM (#36694816)
Ah, just like I do when I press F12 over an operator.
Re:C++23: The Next C++ Standard by enriquto on Wednesday July 12, 2023 @11:04AM (#36695608)
You come across as a bit facetious here.

Of course, the compiler or an advanced IDE can know what your code means. If all your identifiers were random permutations of l and I: lIllI1lI, your IDE would not mind either, but the code would be horrific, don't you agree? The point of the OP is that overloaded operators (and functions) make it harder to reason about the code for a human that reads it. At least for some people. At the end, everything is "just" syntactic sugar, but it makes a significant difference.

Re:C++23: The Next C++ Standard by throwaway894345 on Wednesday July 12, 2023 @02:19PM (#36699277)
Exactly. If you don't care that the code is unreadable and you can rely on every human viewing the code through an IDE with symbol resolution (and not say, online code review platforms) and remembering to use said symbol resolution to check every operator, then operator overloading is great!
Re:C++23: The Next C++ Standard by spacechild1 on Tuesday July 11, 2023 @04:31PM (#36686838)
Actually, I'm quite happy what C++ has to offer :)

Yes, the * operator can be ambiguous in the context of classic vector math (although that is just a matter of documentation), but not so much with SIMD vectors, audio vectors, etc.

Again:

a) vec4 = (vec1 - vec2) * 0.5 + vec3 * 0.3;

or

b) vec4 = plus(mul(minus(vec1, vec2), 0.5), mul(vec3, 0.3));

Which one is more readable? That's pretty much the perfect use case for operator overloading.

Re:C++23: The Next C++ Standard by spacechild1 on Tuesday July 11, 2023 @05:04PM (#36687193)
Regarding the * operator, I think glm got it right: * is element-wise multiplication, making it consistent with the +,-,/ operators; dot-product and cross-product are done with dedicated free functions (glm::dot and glm::cross).
Re:C++23: The Next C++ Standard by cuteboy19 on Wednesday July 12, 2023 @12:40AM (#36690532)
That's what numpy does too.
Re:C++23: The Next C++ Standard by fpoling on Tuesday July 11, 2023 @06:58PM (#36688352)
One never writes such expression in a serious code. Even with move semantic and lazy evaluation proxies it is hard to avoid unnecessary copies. Explicit temporaries make code mode readable and performant:

auto t = minus(vec1, vec2); mul_by(t, 0.5/0.3); add(t, vec3); mul_by(t, 0.3); v4 = std::move(t);

Re:C++23: The Next C++ Standard by Tunabrain on Tuesday July 11, 2023 @08:07PM (#36688810)
I think there may be a misunderstanding here regarding the use case. If the vectors are large and allocated on the heap/on an accelerator, then yes, writing out explicit temporaries may be faster. Of course, this does not preclude operator overloading at all: You could write the same code as auto t = vec1 - vec2; t *= 0.5/0.3; t += vec3; t *= 0.3;

However, if the operands are small (e.g. 2/3/4 element vectors are very common), then "unnecessary copies" or move semantics don't come into play at all. These are value types and the compiler would boil them down to the same assembly as the code you post above. Many modern C++ codebases in scientific computing, rendering, or the game industry make use of vector classes with operator overloading, with no performance drawbacks whatsoever; however, code is much more readable, as it matches actual mathematical notation.

Re:C++23: The Next C++ Standard by spacechild1 on Tuesday July 11, 2023 @08:25PM (#36688945)
Thank you for putting this so eloquently!

> Many modern C++ codebases in scientific computing, rendering, or the game industry make use of vector classes with operator overloading, with no performance drawbacks whatsoever

I guess these people are all not writing "serious code" :-p

Re:C++23: The Next C++ Standard by spacechild1 on Tuesday July 11, 2023 @08:15PM (#36688876)
> One never writes such expression in a serious code.

Oh please, because you know exactly which kind of code I write? I'm pretty sure that with glm::vec3 the compiler can optimize this just fine. Also, "vec" could really be anything, it is just a placeholder.

That being said, if you need to break up your statements, you can do so with operators:

    auto t = vec1 - vec2;
    t *= 0.5/0.3;
    t += vec3;
    t *= 0.3;
Personally, I find this much more readable. But hey, apparently there are people who really prefer free functions. I accept that.
Re:C++23: The Next C++ Standard by _gabe_ on Wednesday July 12, 2023 @12:27AM (#36690476)
TIL Box2D must not be serious code because it doesn't use copious amounts of explicit temporaries[0].

And just for the record, I'm very glad Erin Catto decided to use operator overloading in his code. It made it much easier for me to read and understand what the code was doing as opposed to it being overly verbose and noisy.

[0]: https://github.com/erincatto/box2d/blob/main/src/collision/b...

Re:C++23: The Next C++ Standard by MadcapJake on Tuesday July 11, 2023 @04:31PM (#36686839)
If editors were to implement it, you could navigate to the corresponding overload implementation or even provide some hint text. Just like they do for other functions.
Re:C++23: The Next C++ Standard by pjmlp on Wednesday July 12, 2023 @03:57AM (#36691609)
In Visual Studio it is already there.
Re:C++23: The Next C++ Standard by throwaway894345 on Wednesday July 12, 2023 @02:22PM (#36699348)
Yeah, we would need editors and code review tools to not only follow overloads to their functions but also highlight that the operator is overloaded in the first place. Of course, this is quite a lot more work than just not overloading things in the first place (particularly since the benefit of operator overloading is negligible).
Re:C++23: The Next C++ Standard by eesmith on Tuesday July 11, 2023 @05:42PM (#36687627)
Dealing with money is important, even if it's only a small part of mathematics. I'll focus on that.

Python's 'decimal' module uses overloaded operators so you can do things like:

  from decimal import Decimal as D

  tax_rate = D('0.0765')

  subtotal = 0
  for item in purchase:
     subtotal += item.price * item.count # assume price is a Decimal
  
  taxes = (subtotal * tax_rate).quantize(D('0.00'))
  total = subtotal + taxes
Plus, there's support for different rounding modes and precision. In Python's case, something like "a / b" will look to a thread-specific context which specifies the appropriate settings:

  >>> import decimal
  >>> from decimal import localcontext, Decimal as D
  >>> D(1) / D(8)
  Decimal('0.125')
  >>> with localcontext(prec=2):
  ...   D(1) / D(8)
  ...
  Decimal('0.12')
  >>> with localcontext(prec=2, rounding=decimal.ROUND_CEILING):
  ...   D(1) / D(8)
  ...
  Decimal('0.13')
Laws can specify which settings to use, for examples, https://www.law.cornell.edu/cfr/text/40/1065.20 includes "Use the following rounding convention, which is consistent with ASTM E29 and NIST SP 811",

  (1) If the first (left-most) digit to be removed is less than five, remove all the appropriate digits without changing the digits that remain. For example, 3.141593 rounded to the second decimal place is 3.14.

  (2) If the first digit to be removed is greater than five, remove all the appropriate digits and increase the lowest-value remaining digit by one. For example, 3.141593 rounded to the fourth decimal place is 3.1416.

   ... (I've left out some lines)
and from https://www.law.cornell.edu/cfr/text/7/1005.83 :

  (3) Divide the result in paragraph (a)(2) of this section by 5.5, and round
  down to three decimal places to compute the fuel cost adjustment factor;

  (4) Add the result in paragraph (a)(3) of this section to $1.91;

  (5) Divide the result in paragraph (a)(4) of this section by 480;

  (6) Round the result in paragraph (a)(5) of this section down to five decimal
  places to compute the mileage rate. 
There's probably laws which require multiple and different rounding modes in the calculation.

This means simply doing all of the calculations in scaled bigints or as fractions won't really work.

Now of course, you could indeed handle all of this with prefix functions and with explicit context in the function call, but it's going to be more verbose, and obscure the calculation you want to do. I mean, it's not seriously worse. Compare:

  with localcontext(prec=3, rounding=decimal.ROUND_DOWN):
    line3 = line2 / D("5.5")
  line4 = line3 + D("1.91")
  line5 = line4 / 480
  line6 = line5.quantize(D('.00001'), rounding=decimal.ROUND_DOWN)
vs. some function-based API with overloaded parameter types:

  line3 = decimal_div(line2, D("5.5"), prec=3, rounding=decimal.ROUND_DOWN)
  line4 = decimal_add(line3, D("1.91"))
  line5 = decimal_div(line4, 480)
  line6 = decimal_quantize(line5, D('.00001'), rounding=decimal.ROUND_DOWN)
But it is worse. I also originally made a typo in the function-based API for line5 where I used "decimal_add" instead of "decimal_div" - the symbols "/" and "+" stand out more, and are less likely to be copy&pasted/auto-completed incorrectly.

If overloaded parameters - "spooky action at a distance vibes" - also aren't allowed, then this becomes more rather more complicated.

Re:C++23: The Next C++ Standard by dekhn on Tuesday July 11, 2023 @06:09PM (#36687919)
plus(x, y) IS mathematical notation. It's RPN without symbol shortcuts. Mathematica has used this for decades.
Re:C++23: The Next C++ Standard by danzk on Tuesday July 11, 2023 @06:38PM (#36688193)
It's Polish Notation actually.
Re:C++23: The Next C++ Standard by dekhn on Tuesday July 11, 2023 @06:56PM (#36688332)
Thanks, corrected in place.
Re:C++23: The Next C++ Standard by spacechild1 on Tuesday July 11, 2023 @08:00PM (#36688775)
sure, I was a bit sloppy there, but I think you know what I meant. Polish notation is just not how most people write arithmetic expressions :)
Re:C++23: The Next C++ Standard by kazinator on Tuesday July 11, 2023 @07:07PM (#36688405)
mul(x, y)

can be overloaded! x and y could be any cobination of matrix, vector, or integer, float.

You're mixing up overloading (which is semantics) with syntax.

Re:C++23: The Next C++ Standard by spacechild1 on Tuesday July 11, 2023 @11:34PM (#36690186)
We all know that functions can be overloaded. The emphasis is on operator overloading. So yes, it is about syntax.
Re:C++23: The Next C++ Standard by slavik81 on Tuesday July 11, 2023 @03:23PM (#36686013)
Why have operators at all? If that notation is good enough, then you might as well use it for the built-in types too. We're halfway to designing a Lisp!
Re:C++23: The Next C++ Standard by pjmlp on Tuesday July 11, 2023 @03:24PM (#36686037)
How can you be sure that plus() and mul() actually do what they say?
Re:C++23: The Next C++ Standard by coldpie on Tuesday July 11, 2023 @03:31PM (#36686136)
They're functions. Whatever they do, my code will go execute some code from whatever library implements them, which is what a function does. I just want to be able to rely on [] being an array subscript when I read some unfamiliar code. Is that too much to ask?
Re:C++23: The Next C++ Standard by pjmlp on Tuesday July 11, 2023 @04:28PM (#36686799)
Just like the function that happens to be named [].
Re:C++23: The Next C++ Standard by kristiandupont on Wednesday July 12, 2023 @01:14AM (#36690686)
Can IDE's detect this and offer "go to implementation" on an overloaded operator these days? Because besides from the surprise-element in the fact that there even is code to debug hiding somewhere, not being able to quickly navigate to it is much worse in my opinion. And with infix operators where you can't even be sure which operand the implementation belongs to, figuring it out can be a bit of a detective task.
Re:C++23: The Next C++ Standard by pjmlp on Wednesday July 12, 2023 @02:56AM (#36691238)
Visual Studio does show a popup, go to implementation, and if the implementation happens to be visible on the same screen, also highlights it.
Re:C++23: The Next C++ Standard by circuit10 on Tuesday July 11, 2023 @07:23PM (#36688506)
If you’re using it right then [] is still an array subscript, just with a custom implementation for getting array elements
Re:C++23: The Next C++ Standard by throwaway894345 on Tuesday July 11, 2023 @03:32PM (#36686152)
You read the code. And unlike operator overloading, you know at a glance exactly which implementation to look at. There is no spooky action at a distance.
Re:C++23: The Next C++ Standard by joshuamorton on Tuesday July 11, 2023 @04:05PM (#36686539)
How, the functions might support dynamic dispatch or be overloaded based on types.

There's nothing that prevents me from implementing all of

    plus(Int, Int)
    plus(Int, Vec)
    plus(Int, Mat)
    plus(Vec, Vec)
    plus(Vec, Mat)
    plus(Mat, Mat)
and to know which `plus` is being dispatched, you need to know the types of both arguments, exactly the same as if `plus` is named `__add__` in python or `operator+` in C++.
Re:C++23: The Next C++ Standard by throwaway894345 on Wednesday July 12, 2023 @08:09AM (#36693362)
Yes, overloading is bad (operator overloading is a kind of overloading).
Re:C++23: The Next C++ Standard by joshuamorton on Wednesday July 12, 2023 @04:44PM (#36701398)
How do you do math without overloading? I have ints, floats, bignums, vectors, matrices, complex numbers, and potentially fractions.

You want every operation to have a distinctly named version for each other numeric type?

Maybe you can reduce some of those, but you can't really have interfaces without some overloading.

Re:C++23: The Next C++ Standard by pjmlp on Tuesday July 11, 2023 @04:27PM (#36686784)
Exactly the same thing with functions that happen to use funny characters.
Re:C++23: The Next C++ Standard by throwaway894345 on Wednesday July 12, 2023 @08:09AM (#36693356)
Nah, there’s no spooky action at a distance when you don’t overload. You know concretely what code is running.
Re:C++23: The Next C++ Standard by pjmlp on Wednesday July 12, 2023 @10:11AM (#36694827)
Have you ever heard of linkers?
Re:C++23: The Next C++ Standard by throwaway894345 on Wednesday July 12, 2023 @01:30PM (#36698320)
Have you ever heard of potatoes? Neither of them help you tell at a glance which code is being dispatched to.
Re:C++23: The Next C++ Standard by Etherlord87 on Tuesday July 11, 2023 @03:51PM (#36686400)
I think in this context what's important is if these functions are overloaded or not…
Re:C++23: The Next C++ Standard by pjmlp on Tuesday July 11, 2023 @04:30PM (#36686823)
Which in any case also means the only way to be sure is to track down the implementation, the actual character set hardly makes a difference.
Re:C++23: The Next C++ Standard by enriquto on Tuesday July 11, 2023 @04:51PM (#36687051)
I agree. Function overloading is just as bad as operator overloading.
Re:C++23: The Next C++ Standard by pjmlp on Wednesday July 12, 2023 @02:33AM (#36691087)
You still need to validate if the implementation matches the name, no matter what.
Re:C++23: The Next C++ Standard by ThemalSpan on Tuesday July 11, 2023 @02:46PM (#36685510)
Sorry but please don't take Eigen (https://eigen.tuxfamily.org) away from me. Can't speak for others, but the scientific code I work on would become unreadable like that.
Re:C++23: The Next C++ Standard by sva_ on Tuesday July 11, 2023 @03:33PM (#36686158)
Re:C++23: The Next C++ Standard by bigbillheck on Tuesday July 11, 2023 @03:31PM (#36686123)
Function overloading's not that much better.
Re:C++23: The Next C++ Standard by dleslie on Tuesday July 11, 2023 @04:53PM (#36687080)
Even better:

    (plus 
      (mul vec1 vec2)
      (mul vec3 vec4))
Lisp is love.
Re:C++23: The Next C++ Standard by Koshkin on Tuesday July 11, 2023 @05:56PM (#36687782)
Love is cruel; you could fall for a goat (a Russian proverb)
Re:C++23: The Next C++ Standard by pjmlp on Wednesday July 12, 2023 @03:58AM (#36691614)
I rather type,

    (+
       (* vec1 vec2)
       (* vec3 vec4))
Lisp is love indeed.