5 ms·
> These days you see languages actively trying to distance themselves from OOP as a form of enticement (Rust and Go being the two prime examples). They definit
by xscott 7y ago
> These days you see languages actively trying to distance themselves from OOP as a form of enticement (Rust and Go being the two prime examples).
They definitely distance themselves from inheritance. However both Rust traits and Go interfaces look to me like they're there to appease people who like the .method() calling syntax. (I know there's more to the story than that, particularly for Rust and its type system...) I think either could've gone with multimethods or overloaded functions and have been better for it, but a lot of people seem to really like the object.method() look.
- cgrealy 7y ago> a lot of people seem to really like the object.method() look. There’s a pretty simple reason for that: autocomplete. In most halfway decent dev environments, typing “object.” will present you with a list of possible operations on that object. OTOH, if you want to do “method(object)” you need to know the method name (in all current scopes, including globals). I’m not saying this is the only or even best way to write code, but it’s definitely a factor IMHO.
- xscott 7y agoYou're almost certainly right, this is pretty compelling. It's not the way I work, but I'm sure lots of people really like their IDEs. Despite that, I recently changed one of my APIs from being: result foo(bar b, other stuff) result foo(baz b, other stuff) result foo(bum b, other stuff) To: result r = bar.foo(other stuff) result r = baz.foo(other stuff) result r = bum.foo(other stuff) Not because of the IDE, but because the compiler error messages are horrible for the ones above when you make a mistake. If you pass the type as the first argument, the compiler "helpfully" tells you 3 pages of information about all of the overloads. However, if you use method syntax, it only tells you overloads for the one object. It's a little frustrating that the hammer is changing the shape of our hands, and not vice versa.
- jcelerier 7y ago> It's a little frustrating that the hammer is changing the shape of our hands, and not vice versa. the "hammer" in this case being "doing things in causal order" - hard to find a different tool in the shed.
- xscott 7y agoI don't understand what you mean by "causal" in this context. A.f(B) is just a syntax sugar around f(A, B) unless dynamic dispatch comes into play. Then it's usually syntax sugar around something like: A._vtable[f](A, B) It really is just a function of two arguments, even when the privileged first argument looks like it's outside of the parens.
- jcelerier 7y ago> I don't understand what you mean by "causal" in this context. I mean that if you want contextual autocompletion (e.g. have only the relevant functions being shown given the type of an argument) then at some point you have to give your IDE this type information before typing the function - however you look at it it will always be [context] [function] instead of [function] [context]. > A.f(B) is just a syntax sugar around f(A, B) unless dynamic dispatch comes into play. this is a complete implementation detail of your operating system ABI and the way OO languages compile down to it, and in no way a "general truth" (of course there is an equivalence between both). There used to be some CPUs with hardware instructions for OO method calls in the 80s for instance - you may want to call that syntax sugar around electrons, but I would respectfully disagree :-) (even then, for instance Microsoft has the __thiscall calling convention for C++ methods which differs from __cdecl used for C functions - so even there there are fundamental differences between a.f(b) and f(a, b) as arguments will be passed differently to the method.)
- jstimpfle 7y agoI just don't use overloads, and not even a language that has overloads.
- SamReidHughes 7y agoI’d say infix syntax and somewhat asymmetric method lookup/overloading rules are the bigger factor. Notwithstanding that it matches the implementation of virtual dispatch.
- xscott 7y agoYeah, both Rust and Go also use the method syntax to provide dynamic dispatch, and it's simpler for them since this dispatch is chosen (presumably vtable style) based on the privileged first operand. However, I think using methods to provide infix syntax is a real mistake. For instance, if I want to make an infix operator which allows me to subtract mytype and yourtype, I can do the following: mytype - yourtype; // mytype.sub(yourtype) However, the opposite requires me to add methods to yourtype: yourtype - mytype; // yourtype.sub(mytype) Python gets around this by having 'r' versions of functions which get invoked if yourtype refuses to acknowledge my type. However Go and Rust could resolve which binary function to use statically, but they don't want to support overloading unless it's a method on the first object.
- SamReidHughes 7y agoInfix operators are out of scope of my comment. I am only referring to method invocation syntax itself, x.a(y, z).b(v), being infix.
- xscott 7y agoI misunderstood then. However, if you just meant that people prefer method syntax because method syntax is infix, then it seems to come full circle to asking why they like infix syntax for methods... Doesn't really matter - it is what it is.
- SamReidHughes 7y agoWell, if you imagine some prefix method syntax -- let's say, foo[a](b, c, d) instead of a.foo(b, c, d) -- it misses out on one of the advantages, in that method chaining is easier to read. I mean this is a nice feature for function call syntax in general and the benefits of infix syntax is not specific to methods. This is one reason why some new languages make f(a, b, c) and a.f(b, c) produce the same AST. Anyway, I wanted to separate the effects of infix positioning of the method name from those of having distinguished syntax for method invocations, which with static dispatch still carries other advantages.