Going to the Google's walled garden with Go



  • @dkf said in Going to the Google's walled garden with Go:

    C++ operator abuse also allows for some extremely surprising behaviour when mixed with overloading.

    Especially when you throw some implicit casts and type aliases which resolve to different types depending on #defines into the mix.


  • Discourse touched me in a no-no place

    @Bulb said in Going to the Google's walled garden with Go:

    The proper solution isn't to not support operator overloading, the proper solution is to only allow overloading the related ones together. Or at least delegate them to each other by default, so when you overload +, then += gets defined consistently as x = x + y, and if you define <, then ==, !=, <= and >= get defined correspondingly again.

    The proper solution is that you define .equals() and .cmp() and all the operators use those two. It'll still let you make equality inconsistent with ordering (because some types don't have useful natural orderings), but you don't get to make < and > do totally different things. Want arithmetic ops? Implement a profile of all the implementations and the language will let you have the ops.

    I wouldn't ever overload + to mean string concatenation.


  • Discourse touched me in a no-no place

    @topspin said in Going to the Google's walled garden with Go:

    Operators are just functions with a special syntax (usually symbols instead of letters and infix instead of prefix), nothing else.

    And precedence and associativity.


  • Discourse touched me in a no-no place

    @Bulb said in Going to the Google's walled garden with Go:

    I've been wondering why the async runtime is such a big deal.

    Because it takes message passing as a fundamental primitive, and the receiver construct allows you to receive a value from one of multiple communication channels and take a different action depending on where you got the value from. That's an extremely powerful thing for modelling concurrent systems (with strong theory going back lots of decades). Yes, I'm sure you can do something like that with a library, but having a language construct makes it very available and easy to use. (TBQF having a GC also helps a lot.)

    Or it would do if Go didn't screw the pooch in other ways, especially with mutability.



  • @Bulb said in Going to the Google's walled garden with Go:

    The [] are defined for arrays as indexing by offset,

    Except in C, where index[array] is a thing, although it's one I hope never ever to see in production code. It's a thing because addition of pointers and integers is commutative, and a[b] is exactly identical to *(a+b).



  • @dkf said in Going to the Google's walled garden with Go:

    C++ operator abuse also allows for some extremely surprising behaviour when mixed with overloading.

    I didn't see any surprising behaviour. Of course the argument-dependent lookup is fairly complex in and off itself and combining member and non-member definitions (that can only happen for operators) is a bit of extra complication, but I've never seen that be an actual problem even for iostreams that do mix them like that.

    Languages should not be made out of landmines.

    C'est la C++.



  • @dkf said in Going to the Google's walled garden with Go:

    @Bulb said in Going to the Google's walled garden with Go:

    The proper solution isn't to not support operator overloading, the proper solution is to only allow overloading the related ones together. Or at least delegate them to each other by default, so when you overload +, then += gets defined consistently as x = x + y, and if you define <, then ==, !=, <= and >= get defined correspondingly again.

    The proper solution is that you define .equals() and .cmp() and all the operators use those two.

    If I understand correctly, C++20 started to do that, except it still calls the first operator==() and the second got a new name of operator<=>() (a.k.a spaceship operator).

    It'll still let you make equality inconsistent with ordering (because some types don't have useful natural orderings), but you don't get to make < and > do totally different things.

    You can have equality default to .cmp() == Equal. I think C++20 does that.

    Want arithmetic ops? Implement a profile of all the implementations and the language will let you have the ops.

    I wouldn't ever overload + to mean string concatenation.

    It is associative and has a zero element (empty string), so it's a perfectly cromulent addition.



  • @dkf said in Going to the Google's walled garden with Go:

    @topspin said in Going to the Google's walled garden with Go:

    Operators are just functions with a special syntax (usually symbols instead of letters and infix instead of prefix), nothing else.

    And precedence and associativity.

    Lets throw infix functions in the mix just for fun as well.



  • @Carnage Aaaaaand this is the last straw for me as far as this thread is concerned.



  • @Carnage said in Going to the Google's walled garden with Go:

    @dkf said in Going to the Google's walled garden with Go:

    @topspin said in Going to the Google's walled garden with Go:

    Operators are just functions with a special syntax (usually symbols instead of letters and infix instead of prefix), nothing else.

    And precedence and associativity.

    I forgot associativity, true. Precedence isn't a property of addition though, it is a relative property of different operators, and only matters when multiple binary operations are defined for the types.

    Lets throw infix functions in the mix just for fun as well.

    They've been in since long ago. That's what (binary) operators are.


  • Discourse touched me in a no-no place

    @Bulb said in Going to the Google's walled garden with Go:

    I didn't see any surprising behaviour.

    Maybe you manage your expectations better, but I found that when you have two overloads (one taking a std::string and the other a bool) then you get real fun (:fun:) when you pass a string constant, and the type-casting operators are fully able to leverage that stuff to the fullest.


  • Discourse touched me in a no-no place

    @Bulb said in Going to the Google's walled garden with Go:

    @Carnage said in Going to the Google's walled garden with Go:

    @dkf said in Going to the Google's walled garden with Go:

    @topspin said in Going to the Google's walled garden with Go:

    Operators are just functions with a special syntax (usually symbols instead of letters and infix instead of prefix), nothing else.

    And precedence and associativity.

    I forgot associativity, true. Precedence isn't a property of addition though, it is a relative property of different operators, and only matters when multiple binary operations are defined for the types.

    Lets throw infix functions in the mix just for fun as well.

    They've been in since long ago. That's what (binary) operators are.

    C++ only allows a limited set of operators. It doesn't constrain the semantics of their implementations much, but the syntactic forms and the rules to do with associativity and so on are fixed.

    There are other languages that you define your own operators. They're much better for expressing mathematics.



  • @dkf said in Going to the Google's walled garden with Go:

    Implement a profile of all the implementations

    Which 'all the implementations'? Some types support multiplication but not division (matrices). If you distinguish between points and vectors, the former will allow subtraction, but not addition. But you want to be able to add a vector to a point. Etc.



  • @dkf said in Going to the Google's walled garden with Go:

    @Bulb said in Going to the Google's walled garden with Go:

    I didn't see any surprising behaviour.

    Maybe you manage your expectations better, but I found that when you have two overloads (one taking a std::string and the other a bool) then you get real fun (:fun:) when you pass a string constant, and the type-casting operators are fully able to leverage that stuff to the fullest.

    User-defined conversions (and some default ones) can indeed wreak serious havoc in C++ code, and the lesson has been to avoid defining them like a plague. But the problem is with conversions and impacts methods, static multimethods (overloaded free functions) and operators (which can be either) alike.

    @dkf said in Going to the Google's walled garden with Go:

    There are other languages that you define your own operators.

    Unfortunately Rust isn't one of them.


  • Discourse touched me in a no-no place

    @cvi said in Going to the Google's walled garden with Go:

    @dkf said in Going to the Google's walled garden with Go:

    Implement a profile of all the implementations

    Which 'all the implementations'? Some types support multiplication but not division (matrices). If you distinguish between points and vectors, the former will allow subtraction, but not addition. But you want to be able to add a vector to a point. Etc.

    But vectors support multiple types of product too; the dot product, the cross product, and (for good measure) the cartesian product. Which one are you going to have be your sole * operator? You can't differentiate between them by argument type...



  • @stillwater Off the wall answer here. Demanding a weird language for no reason might be making a case for "labor shortages." I once interviewed at a place using Go that was cheap enough to beg C# people to learn on the job instead of paying for Go people. They did what every other place that puts me through a code test does: see Pascal casing and have a fucking seizure.



  • @Zenith said in Going to the Google's walled garden with Go:

    Go […] see Pascal casing and have a fucking seizure.

    Funny, because public identifiers in Go use Pascal casing. In fact, it is what makes them public.

    Because Go does not have any keywords for public and private. Instead everything that starts with uppercase character is public and everything that starts with lowercase one is private (to module; Go's element of privacy is a module).

    Side note: Go isn't the first language where case distinction has a semantic significance. In Haskell, identifiers with uppercase initial character are type- or typeclass-constructors and identifiers with lowercase initial character are values (including type variables). With : (and maybe ,, not sure) being considered uppercase. It sidesteps the problem that makes C to not be a context-free grammar.


  • BINNED

    @Bulb and people complain about python using whitespace. At least identifier naming is only convention and not semantic.

    Also, who cares about context free. C++ grammar isn’t even decidable. :half-trolleybus-l:



  • @topspin said in Going to the Google's walled garden with Go:

    @Bulb and people complain about python using whitespace. At least identifier naming is only convention and not semantic.

    Haskell also uses significant whitespace. And both that, and the uppercase/lowercase distinction serve to make it more readable. It has plenty of other things to make it less readable though. Like the function application operator $. IIRC the definition is simply f $ g = f g, except it has lowest priority and direct function application has highest. Or currying, which leads to functions that logically take three arguments, and the type signature, if present, shows that, but the definition only has one. That is some serious mind-bender.



  • @dkf said in Going to the Google's walled garden with Go:

    Burnt out ex-googlers.

    I'm a burnt-out ex-googler; now I use DuckDuckGo.



  • @topspin
    Go: stupid
    Python: stupid


  • Discourse touched me in a no-no place

    @Zenith Remember this:
    Computers: stupid.



  • @Bulb said in Going to the Google's walled garden with Go:

    everything that starts with uppercase character is public and everything that starts with lowercase one is private

    Oh, that's a very creative idea, totally new!
    Fortran used the first character of a variable name to determine its type. Wow! Let's return to the 1950ies!


  • BINNED

    @BernieTheBernie Fortran: stupid.



  • PHP: stupid



  • Shouldn't we generify this?

    class ProgrammingLanguage {
      public bool isStupid() {
        return true;
     } 
    } 
    


  • @BernieTheBernie said in Going to the Google's walled garden with Go:

    @Bulb said in Going to the Google's walled garden with Go:

    everything that starts with uppercase character is public and everything that starts with lowercase one is private

    Oh, that's a very creative idea, totally new!

    It does not have to be creative. And it's not that bad. It saves a keyword and the convention makes more sense than many other naming conventions out there.



  • @Parody Yes. Every programming language that had a class keyword so far was stupid. There may be some that are not stupid, but it's not the ones with class keyword.

    … unfortunately that fails to say anything about Go, which does not have said keyword.


  • Considered Harmful

    @Bulb said in Going to the Google's walled garden with Go:

    It saves a keyword

    By Grabthar's Hammer...



  • @topspin said in Going to the Google's walled garden with Go:

    I don't know a better option than Java that has a similar record of being long-term stable / maintained.

    C# was designed specifically to be "a better Java," and they've done quite a good job at fulfilling that goal.


  • BINNED

    @Mason_Wheeler nobody runs windows on their server.

    Also, from what I read here, they’re rewriting it every 3 years.



  • @topspin said in Going to the Google's walled garden with Go:

    nobody runs windows on their server.

    That hasn't been a valid objection for something like 5 years now. Core is fully cross-platform.

    Also, from what I read here, they’re rewriting it every 3 years.

    :wtf_owl: Where'd you read that?


  • BINNED

    @Mason_Wheeler 5 years is nothing compared to Java / C / C++.



  • @Mason_Wheeler said in Going to the Google's walled garden with Go:

    fulfilling

    Why did I read failfilling frist?



  • @topspin And if that's the only data point you look at, you'd have a point there. But why in the world should it be? We're not living in the world of 6 years ago; we're living today, and the options of today are available to us.


  • BINNED

    @Mason_Wheeler you’re ignoring the context of the question. :surprised-pikachu:



  • @topspin Not ignoring, more like lost. How many 8a9370f3-58a3-4c9e-8ce3-fc9dcb9c30e3-image.pngs back do I have to look before I find a question being asked?



  • @topspin sadly that's not actually true; I know a sadly large number of people running Windows on their servers, for various reasons.

    The web server people, though, have no fucking excuses.

    And there are rumours, yes, of Windows back-pedalling from this every-6-months-rolling-release back towards a 3 year cycle, although with a stopover at 'annual' along the way somewhere.



  • @Mason_Wheeler said in Going to the Google's walled garden with Go:

    @topspin said in Going to the Google's walled garden with Go:

    I don't know a better option than Java that has a similar record of being long-term stable / maintained.

    C# was designed specifically to be "a better Java," and they've done quite a good job at fulfilling that goal.

    It did manage to do a couple things better, but overall that's not a very ambitious goal.

    Granted, Go set out to be a better C++ and managed to be a … different Java.



  • @Bulb said in Going to the Google's walled garden with Go:

    @Mason_Wheeler said in Going to the Google's walled garden with Go:

    @topspin said in Going to the Google's walled garden with Go:

    I don't know a better option than Java that has a similar record of being long-term stable / maintained.

    C# was designed specifically to be "a better Java," and they've done quite a good job at fulfilling that goal.

    It did manage to do a couple things better, but overall that's not a very ambitious goal.

    Granted, Go set out to be a better C++ and managed to be a … different Java.

    Java was already pretty good. Go is strictly worse than Java (or C#) due to its lack of OO support.


  • Notification Spam Recipient

    @topspin said in Going to the Google's walled garden with Go:

    @Mason_Wheeler nobody runs windows on their server.

    A lot of companies, including large international corporations and banks, run windows servers.

    A lot of this happened to me last few years:

    👴: We must rewrite everything in Core as fast as possible.
    👦🏻: Why?
    👴: So we can host everything on Lunix.
    👦🏻: Will we though?

    The answer is no, move to linux doesn't happen. So the hysterical push for rewrites died down somewhat recently.

    What's left as a result is projects running on 7 different versions of .Net. Neverending wasteland of partly rewritten partly abandoned shit I have to wade through every day.



  • @Mason_Wheeler said in Going to the Google's walled garden with Go:

    Java was already pretty good. Go is strictly worse than Java (or C#) due to its lack of OO support.

    Java isn't very good. It's mediocre. It is mediocre by design and proud of it, but that's still mediocre.

    But if there is one thing in which Go is better than Java, it is the way it does OO. Go does not have inheritance, because inheritance is considered harmful. The other OO things it does have, and it did the right thing of untangling them, so types are units of data, modules are unit of encapsulation and functions are units of code reuse, instead of shoehorning all three purposes onto classes.



  • @Bulb said in Going to the Google's walled garden with Go:

    Go does not have inheritance, because inheritance is considered harmful. The other OO things it does have

    This is a ridiculous statement. Inheritance is the sine qua non of OO, the thing that made it into the world-conquering paradigm it's become; without it you do not have OO, you just have slightly weird procedural programming.

    The "inheritance is considered harmful" claims invariably fall into one of two categories. 1) Pointing out problems with the way C++ does inheritance -- which is severely broken in many ways that do not apply to basically any other OO language -- and acting like they're problems with OO in general, or 2) blatant misrepresentations by the FP crowd to cut down the competition.

    Go's OO support is terrible because it lacks classes, it lacks inheritance, and it has interfaces but does everything related to interfaces wrong. It's literally hard to conceive of a way to do interfaces worse than Go without deliberately setting out to make an esolang or troll language.


  • BINNED

    @Mason_Wheeler you don’t even need class-based inheritance for OOP. JS does prototypical inheritance, for example.
    Also, you don’t need inheritance (subclassing) for subtyping. It’s mixing up several different things into one (subtyping, encapsulation, code reuse).

    Thinking of OOP as a silver bullet is as 90s as Java and its AbstractFactoryFactoryBeans.



  • @Mason_Wheeler Inheritance didn't make OO a world-conquering paradigm, it is being abandoned as a huge mistake. Interfaces are the important feature.

    The critique of it does not come from either C++ of FP folks, it comes from the advanced Java and C# programmers who are finally realizing it is just a special variant of composition and that the code ends up being easier to understand and maintain if normal composition is used instead.

    And I don't agree Go does interfaces so poorly either. The ‘static duck typing’ approach makes things somewhat prone to conflicts, which is a general theme in Go, but at least it gets one thing right: it allows extending existing types with new interfaces.

    The thing is, the promise of great code reuse never materialized in Java the way it was envisioned and hoped for. Because when you want to combine some pieces of code, the interfaces provided by one are useless to the other that has its own instead, and even if you can adapt them, you have to create adapters everywhere and it quickly stops being worth the trouble.

    But people claim the code reuse did materialize in Julia. Julia also has methods defined outside the types they operate on, and has multiple dynamic dispatch (like CLOS). So now if you want to combine things from two different libraries, you just add a missing method here and there, or a new overload combining types from the different libraries. It makes combining things easier, so it gets done a lot more.



  • @topspin said in Going to the Google's walled garden with Go:

    @Mason_Wheeler you don’t even need class-based inheritance for OOP. JS does prototypical inheritance, for example.

    d26e5c62-ae37-400f-8c97-81bea0fe3343-image.png



  • @Bulb said in Going to the Google's walled garden with Go:

    @Mason_Wheeler Inheritance didn't make OO a world-conquering paradigm, it is being abandoned as a huge mistake.

    Gotta love the passive voice there. Who exactly is proclaiming it "a huge mistake" and abandoning it? Not the people producing serious projects that deal with significant levels of complexity!

    The critique of it does not come from either C++ of FP folks, it comes from the advanced Java and C# programmers who are finally realizing it is just a special variant of composition and that the code ends up being easier to understand and maintain if normal composition is used instead.

    The "prefer drills over hammers" argument again? Seriously? :rolleyes:

    Inheritance is not composition. Composition is not inheritance. A hammer is not a drill. A drill is not a hammer. Anyone attempting to equate the two or assert that one is substitutable for the other is :crazy: and should be dismissed as such without further discussion.

    And I don't agree Go does interfaces so poorly either. The ‘static duck typing’ approach makes things somewhat prone to conflicts, which is a general theme in Go, but at least it gets one thing right: it allows extending existing types with new interfaces.

    That's nowhere near "right." One reason it's insanely wrong becomes apparent the minute you consider the existence of ambiguous words: that Foo method on type Bar doesn't mean the type of Fooing that the interface creator intended! The other reason is that duck typing of any kind is unperformant, and this is one of the major reasons why Go code tends to be slow. (And that's not even getting into the more arcane gotchas of Go's thoroughly broken interface system, such as having a non-nil interface that becomes a nil when cast to its real type!)

    The thing is, the promise of great code reuse never materialized in Java the way it was envisioned and hoped for.

    The people doing the envisioning back in the 90s had never heard of package managers. So we didn't get what they envisioned; instead we got something even better.

    But people claim the code reuse did materialize in Julia. Julia also has methods defined outside the types they operate on

    So does C#. It's called "extension methods" and, among other things, it's what makes stuff like LINQ possible.



  • @Mason_Wheeler said in Going to the Google's walled garden with Go:

    The other reason is that duck typing of any kind is unperformant

    Really? Looking at the way Go's does duck typing seems to be implementable efficiently, certainly without much more overhead compared to other implementations of virtual functions. (FWIW- I tried finding something to back the claim of Go's duck typing being a performance hazard briefly, but didn't find anything. I don't do Go, so I might not have searched for the right things. Links appreciated.)

    In addition, C++ essentially allows for duck typing w.r.t. templated types. Calls are resolved at compile-time, so there's no run-time overhead. In fact, it guarantees that inlining is possible, so it's even possible to skip the overhead of the function call in the first place.

    You can use the above mechanism to get something like Go's interfaces. Run-time overhead is then pretty much that of an indirect function call (which can potentially be improved on by LTO / de-virtualization, if you build with those enabled).



  • @Mason_Wheeler said in Going to the Google's walled garden with Go:

    @Bulb said in Going to the Google's walled garden with Go:

    @Mason_Wheeler Inheritance didn't make OO a world-conquering paradigm, it is being abandoned as a huge mistake.

    Gotta love the passive voice there. Who exactly is proclaiming it "a huge mistake" and abandoning it? Not the people producing serious projects that deal with significant levels of complexity!

    Those don't proclaim anything, they are just not using much inheritance any more. The newer code I've seen, and newer frameworks I've seen, are all interfaces, dependency injection and attributes, but almost no actual inheritance. Except in UI, it is still holding out there occasionally.

    The critique of it does not come from either C++ of FP folks, it comes from the advanced Java and C# programmers who are finally realizing it is just a special variant of composition and that the code ends up being easier to understand and maintain if normal composition is used instead.

    The "prefer drills over hammers" argument again? Seriously? :rolleyes:

    Inheritance is not composition. Composition is not inheritance. A hammer is not a drill. A drill is not a hammer. Anyone attempting to equate the two or assert that one is substitutable for the other is :crazy: and should be dismissed as such without further discussion.

    Inheritance does two things: bring the members of the base into the object, which is composition, and implements all interfaces by delegating to that base, which is almost always wrong due to Liskov substitution principle. Liskov substitution principle means you can almost never actually inherit a class that has some mutating operations.

    And I don't agree Go does interfaces so poorly either. The ‘static duck typing’ approach makes things somewhat prone to conflicts, which is a general theme in Go, but at least it gets one thing right: it allows extending existing types with new interfaces.

    That's nowhere near "right." One reason it's insanely wrong becomes apparent the minute you consider the existence of ambiguous words: that Foo method on type Bar doesn't mean the type of Fooing that the interface creator intended!

    Like in any duck-typed system, the user is responsible for not casting the object to that interface if the method with matching signature does not actually follow the desired semantics. I am not big fan of that bit.

    The other reason is that duck typing of any kind is unperformant, and this is one of the major reasons why Go code tends to be slow.

    Except it isn't, because this is static. C++ does exactly the same thing in templates, does it a lot there, and is still the fastest of the bunch.

    (And that's not even getting into the more arcane gotchas of Go's thoroughly broken interface system, such as having a non-nil interface that becomes a nil when cast to its real type!)

    A lot is broken in Go. But in this particular point, it is still better than Java. And then C#.

    The thing is, the promise of great code reuse never materialized in Java the way it was envisioned and hoped for.

    The people doing the envisioning back in the 90s had never heard of package managers. So we didn't get what they envisioned; instead we got something even better.

    But people claim the code reuse did materialize in Julia. Julia also has methods defined outside the types they operate on

    So does C#. It's called "extension methods" and, among other things, it's what makes stuff like LINQ possible.

    No, it does not. Extension methods allow adding methods to interfaces, but as far as I can tell they don't allow taking type Foo from library and stating that from now on it also implements interface Baz like this. And then taking type Bar from library and also stating from now on it implements interface Baz like that.

    An extension method can be defined for a specific interface, but it is still a static method dispatched statically. If you extend Foo with method meth and Bar with method meth, it does not provide you any way to pass either Foo or Bar at runtime. Or even write a generic that will take either.

    But both Julia and Go let you do that. And that is the part in which Go is better than Java and C#. It is worse in other parts, but this is what I was referring to that it did ‘right’.



  • @cvi said in Going to the Google's walled garden with Go:

    @Mason_Wheeler said in Going to the Google's walled garden with Go:

    The other reason is that duck typing of any kind is unperformant

    Really? Looking at the way Go's does duck typing seems to be implementable efficiently, certainly without much more overhead compared to other implementations of virtual functions. (FWIW- I tried finding something to back the claim of Go's duck typing being a performance hazard briefly, but didn't find anything. I don't do Go, so I might not have searched for the right things. Links appreciated.)

    In addition, C++ essentially allows for duck typing w.r.t. templated types. Calls are resolved at compile-time, so there's no run-time overhead. In fact, it guarantees that inlining is possible, so it's even possible to skip the overhead of the function call in the first place.

    You can use the above mechanism to get something like Go's interfaces. Run-time overhead is then pretty much that of an indirect function call (which can potentially be improved on by LTO / de-virtualization, if you build with those enabled).

    Not really. Types are not declared as implementing an interface. This means that at any place where an interface (rather than a concrete type) is passed as a parameter (or exists as a member of a struct) the code using it has no idea which actual calls are being made. To make this function on a bits-and-bytes level you need to wrap the actual value in a wrapper which makes everything indirect and adds memory overhead to boot. (And also explains how you can have a non-nil interface that's actually a nil value, which is still a massive :wtf:!)


Log in to reply