Hacker News new | past | comments | ask | show | jobs | submit login

This is a well-written objection and I completely agree. Operator overloading is consistently one of the worst ideas I see in programming languages, C++ being the prime offender. Programmers think they're being clever when they write crap like

  boostfs::path path("/some/path");
  path /= "yourfile.txt";
Meanwhile, reading your code without being familiar with boostfs::path, my brain grinds to a halt while I try to understand what in the hell dividing by a string is supposed to do.

This is one of those "well intentioned" features that turns into a quagmire in practice.




> Operator overloading is consistently one of the worst ideas I see in programming languages

Except when not having it is the worst. Working with BigDecimal in Java is hell because it does not have operative overloading, meaning while you avoid

    boostfs::path path("/some/path");
    path /= "yourfile.txt";
you get saddled with bullshit like

    x.add(x.add(ONE).pow(2).subtract(x))


Now this is ugly:

    x.add(x.add(ONE).pow(2).subtract(x))
Especially because sometimes it is difficult to decide whether you should have x.foo(y) or y.foo(x). However,

    add(x, sub(pow(add(x, ONE), 2), x))
Is perfectly acceptable in my opinion (add some indentation if it's difficult to read). As long as you can pass parameters and return values without tricks (ie. passing pointers), this is quite nice. You should even be able to do this in Java with "import static". Or C++ with function overloading.

I'm definitely not a fan of C++ -style operator overloading, where you only have a finite amount of operators with set precedence/associativity and then they get overloaded to meanings that the symbols don't convey. On the other hand, I'm not sure what to think of Haskell either (where you can define arbitrary operators, like >>=, <+> or <$>). It's not as limited as C++, but there are some quite nasty examples that have gone overboard with operators.

Overall, it seems like operator overloading adds a lot of complexity to a language but the benefit is arguable.


However

I comletelly agree with this, it is very confusing API.

You should even be able to do this in Java with "import static"

Yep, you should define a static methods add, sub, pow etc with BigDecimal parameters and then use import static.

Something along

class BigDecimalMath { public static BigDecimal add(BigDecimal x1, BigDecimal x2) { return x1.add(x2); } }

Also JVM JIT would very likely inline such code, so you should not really worry about an added method call.


Too bad you can't just call functional interfaces in Java 8, or you could just do something like this:

BiFunction<BigDecimal, BigDecimal, BigDecimal> add = BigDecimal::add; add(x, y);

Unfortunately you have to do:

add.apply(x, y);

Which is kind of cumbersome.


Yes, it pretty much defeats the point. It would work if they had implemented a default method for an interface.

Unfortunately they brought something completely different in the name of Default Methods, and that was in my opinion not needed.


Eh, there's already no language symbols for exponentiation (unless you're really going to overload XOR in which case you're already part of the problem).


    func SameSideOfTriangle<N>(p0 Point2D<N>, p1 Point2D<N>, a Point2D<N>, b Point2D<N>) bool where N: Number {
        ba := Point2D(b.x.Sub(a.x), b.y.Sub(a.y))
        p0a := Point2D(p0.x.Sub(a.x), p0.y.Sub(a.y))
        p1a := Point2D(p1.x.Sub(a.x), p1.y.Sub(a.y))
        cp0 := ba.x.Mul(p0a.y).Sub(p0a.x.Mul(ba.y))
        cp1 := ba.x.Mul(p1a.y).Sub(p1a.x.Mul(ba.y))
        dot := cp0.x.Mul(cp1.x).Add(cp0.y.Mul(cp1.y))
        return dot >= 0
    }
Versus:

    func SameSideOfTriangle<N>(p0 Point2D<N>, p1 Point2D<N>, a Point2D<N>, b Point2D<N>) bool where N: Number {
        ba := Point2D(b.x - a.x, b.y - a.y)
        p0a := Point2D(p0.x - a.x, p0.y - a.y)
        p1a := Point2D(p1.x - a.x, p1.y - a.y)
        cp0 := ba.x * p0a.y - p0a.x * ba.y
        cp1 := ba.x * p1a.y - p1a.x * ba.y
        dot := cp0.x * cp1.x + cp0.y * cp1.y
        return dot >= 0
    }


I didn't mean to imply that it is never useful, just that it's not worth the baggage in my experience.

Genuinely asking: Why does this function need to be generic over Numbers? Couldn't you implement it once or twice for whatever actually-numeric types you need? How many different Point2D template instantiations do you actually have?


In my project, i32, u32, f32, and f64. Possibly others.

And that's only a small function (only one part of what's needed to check point-triangle intersection!) Copying and pasting e.g. Sutherland-Hodgman clipping or 4D/5D matrix math would get unsustainable quickly.


My experience from graphics programming is that, yes, this is a subdomain where you want both the ability to shorten your functions with overloading and the ability to make your underlying types dynamic. There's a lot of fiddly-bit optimization in that space that is both necessary and much easier to do if you can transition quickly and smoothly from one type to another without having to boil an ocean of declarations.

Granted, you then have the problem of having to deal with overflow on different types, but that's just one of the ocean liner of problems you've signed up for if you're working in the graphics space. May the odds be ever in your favor. ;)


`.pow` is the least problematic part of my example by a fairly long shot. Even keeping it, with operator overloading you'd get

    x + ((x + ONE).pow(2) - x)
or

    x + (pow(x + ONE) - x)


Yes, boost has a couple of instances of eyebrow-raising uses of operator overloading. Including stream operators, that still does not prove that operator overloading is an anti-feature.

On the contrary, when all you have is a couple of examples that aren't even that bad and a slippery slope argument, you are on very thin ground, rhetorically speaking.


It's clearly a balancing-act thing. My scales tip to the hater side.


I am certain that if you did some game programming in a language without OO, you would quickly change your mind.


It's funny. I know very little about C++ and my first thought was that this was, essentially, a shortcut for something like:

    path += '/' + 'yourfile.txt';
It's also entirely possible that I'd already subconsciously read ahead and so knew that it couldn't have possibly been division.


It's easy in a little two-line snippet discovered in the context of a discussion about operator overloading. It's much less easy to understand when grepping for a config file name (which is where I found it) where you don't even have the declaration of the variable to give you the context that you're dealing with anything other than char*.




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

Search: