News for hackers. Stuff that matters.
Re:Is Go Duck-Typed? by zwieback on Wednesday March 04, 2020 @02:34PM (#22487142)
I like the histogram showing how much more frequent interfaces with just one or two methods are.

How frequent are problems where classes unintentionally use an interface due to identical signatures? Seems likely to happen in theory if so many interfaces have few functions.

Re:Is Go Duck-Typed? by jerf on Wednesday March 04, 2020 @03:12PM (#22487535)
I've been programming for over 20 years primarily in languages in which this can nominally happen, and I think I've literally never seen it happen. I've been in situations where I expected a method and it was missing (e.g., Python code expecting a vaguely-file-like object to have seek, receives a socket), but that's the closest to this sort of thing I think I can say I've come. I've never had a case where I called a method that I expected to be one sort of thing, and got a method that was actually a completely different thing.

I used to worry about this sort of thing, but now I think that any language designer making a new language should just consider it a non-issue. If you happen to make it impossible for some other reason, sure, cool, I guess, but don't put even a little bit of effort into worrying about this case.

(Note I'm not saying it can't happen, or even be bad if it did. I'm sure it's happened to some people, and of that set, I'm sure there must be at least one person with a story of how it went really badly wrong somehow. I'm just saying that compared to the sorts of issues that affect every programmer in your language every day, worrying about this edge case is not productive. If this is your biggest problem in your language, or even fits into the top 100, please let me know about your surprisingly perfect language as I am very interested in using it.)

Re:Is Go Duck-Typed? by lmm on Wednesday March 04, 2020 @07:55PM (#22489889)
A bug I've seen more than once is: a function is passed what it thinks is an immutable list, expects to be able to iterate over it repeatedly and get the same items in the same order, but what it actually has is a read-only view backed by a list that is being mutated by a parallel thread. Another bug I've seen repeatedly is a function that assumes it's passed a sorted list, but actually gets passed an unsorted list.

The problem with structural typing isn't that completely unrelated things end up sharing a type. It's that you have no way to express the subtle distinctions between similar but different things, which are actually when having distinct types is most important.

Re:Is Go Duck-Typed? by throwaway894345 on Wednesday March 04, 2020 @04:21PM (#22488274)
My experience is the same.

It's interesting to me that people are very concerned about Go's structural subtyping, but they generally have no qualms about passing functions around. Given that the contract for a function is the function signature, and the contract for an interface is [all function signatures and their associated names], surely the latter is less error prone?

Re:Is Go Duck-Typed? by lmm on Wednesday March 04, 2020 @07:59PM (#22489911)
A function explicitly has no further semantics than its inputs and outputs. The receiving function shouldn't, and won't, assume anything about what the function it's passed does. Whereas when you're passed a bundle of named functions that (presumably) share state between them, it's very natural to assume that this implies relationships between how those functions will behave (even in the simplest examples, e.g. Java's Iterator has only two methods, but it also specifies a relationship between what those two methods return). And so it's very easy to write code that relies on those relationships, and then breaks when passed a bundle of named functions that does not conform to that relationship.
Re:Is Go Duck-Typed? by jerf on Thursday March 05, 2020 @09:25AM (#22493699)
"then breaks when passed a bundle of named functions that does not conform to that relationship."

In that case, the fault lies with the thing that put unrelated functions together and passed them to a thing expecting them to be related. I'm not claiming that structural typing will somehow prevent programmers from deliberately writing wrong things, because I mean, what type system can make that promise? My point is that it is in my experience very rare for something expecting "something that can write bytes" to be accidentally passed "something that can write novels" or something equally unrelated, and then the world blows up in a bad way (that is, not just an exception thrown, because that's just a risk of dynamic languages, but actual bad things happening).

Dynamic languages can at least still get what I said wrong; in Go it's even harder because as others have pointed out, Write([]byte) and Write(NovelInput) Novel still can't cross. But even in dynamic languages, this isn't a problem I had.

Re:Is Go Duck-Typed? by lmm on Thursday March 05, 2020 @10:01AM (#22494024)
Nothing can go wrong with a single function like Write - but in that case you don't need a structural type, you can just pass a write function. As soon as you pass a bundle of two or more functions, you're implying a relationship between them, and it's then easy to accidentally violate that relationship - for instance, a structural type will almost always admit a mutable implementation where the relationship breaks down if the thing is mutated in between calls to the two functions.
Re:Is Go Duck-Typed? by throwaway894345 on Thursday March 05, 2020 @11:42AM (#22495181)
Like I said elsewhere in the thread, you can have the same problem with callbacks:

    func receiver(next func() bool, getItem func() Foo) {}
vs

    type FooIter interface { Next() bool; Item() Foo }

    func receiver(iter FooIter) {}
You're more likely to pass the wrong set of callbacks than to pass the wrong FooIter implementation since the type system doesn't require the callbacks to be related to the same data nor named any certain way.
Re:Is Go Duck-Typed? by lmm on Thursday March 05, 2020 @06:38PM (#22499609)
Replied elsewhere.
Re:Is Go Duck-Typed? by throwaway894345 on Thursday March 05, 2020 @11:40AM (#22495159)
You have the same issue if you pass multiple callbacks to a given function, except as previously mentioned, you're more likely to pass the wrong set of callbacks than to pass the wrong interface implementation (because interface methods are named must be bound to the same data).
Re:Is Go Duck-Typed? by lmm on Thursday March 05, 2020 @06:37PM (#22499607)
Right, but you don't do that. The people who are happy passing functions around are not happy passing multiple callbacks around, by and large. (For example, rather than separate hasNext and next functions, you'd have a single function that returned an optional value).
Re:Is Go Duck-Typed? by throwaway894345 on Friday March 06, 2020 @10:20AM (#22504222)
I agree, but we're arguing about a hypothetical bug scenario that has probably never ever happened: accidentally implementing this sort of stateful interface. These stateful interfaces are already very rare (at least in Go) and accidentally implementing an interface is itself vanishingly rare (I don't think I've ever heard of this happening in practice). It doesn't make sense to me to take grievance with Go's interfaces (which offer strictly stronger protections than callbacks), but be okay with naked callbacks on the basis that "people tend not to use them in a stateful manner" (never mind the stateless scenario in which bare callbacks also provide weaker guarantees than Go's interfaces).
Re:Is Go Duck-Typed? by lmm on Saturday March 07, 2020 @08:33PM (#22515659)
> we're arguing about a hypothetical bug scenario that has probably never ever happened: accidentally implementing this sort of stateful interface. These stateful interfaces are already very rare (at least in Go)

Any interface can be implemented by something that's accidentally (or intentionally) stateful - there's nothing that prevents that in Go. And a interface containing more than one function almost certainly has an implicit relationship between those functions that an implementation could accidentally violate.

> and accidentally implementing an interface is itself vanishingly rare (I don't think I've ever heard of this happening in practice).

Accidentally implementing an interface must happen all the time - not in the sense of unrelated functions having a name collision, but in the sense of implementing a type that looks like another type and not explicitly looking through all the interfaces that other type implements and what expectations they carry.

> It doesn't make sense to me to take grievance with Go's interfaces (which offer strictly stronger protections than callbacks), but be okay with naked callbacks on the basis that "people tend not to use them in a stateful manner" (never mind the stateless scenario in which bare callbacks also provide weaker guarantees than Go's interfaces).

Naked callbacks make it clear what the expectation is and where the responsibility lies. It's the same as the idea that bad encryption is worse than no encryption at all, or a datastructure that is "mostly threadsafe" is worse than one that immediately errors when used from multiple threads.

Re:Is Go Duck-Typed? by seertaak on Wednesday March 04, 2020 @03:36PM (#22487761)
I think it's really interesting to contrast this with Stroustroup's advice on concepts: "My rule of thumb is to avoid “single property concepts.”".

He gives an excellent example leading to this dictum: single property interface "Drawable". Let Shape implement drawable with widely expected results. But: let Cowboy implement Drawable -- now with disastrous consequences. The solution to this dilemma is as stated above, not to allow concepts/interfaces/{wave hands} to refer to a single object -- for example, a number is a concept which refers to multiple operations (i.e. methods).

To my mind, this feels right, albeit initially surprising. It makes me think that e.g. the "Neg" interface in Rust is wrong.

See: http://www.stroustrup.com/good_concepts.pdf

Re:Is Go Duck-Typed? by loopz on Wednesday March 04, 2020 @03:56PM (#22487993)
For golang, single method interfaces are recommended as preferable. The problem with huge interfaces or classes is they can always become larger! While if you compartmentalize single responsibility into separate components (may be several methods if applicable to the domain and design), each reason to change may be restricted to that bounded context only. If not, it's a signal design might need revisit.

The problem with "Drawable" is that it is property/data, and not messaging-based, thus breaking encapsulation, even if it was behaviour-based.

Re:Is Go Duck-Typed? by seertaak on Wednesday March 04, 2020 @04:01PM (#22488045)
> The problem with huge interfaces or classes is they can always become larger!

That's definitely a good reason to prefer small interfaces; I hadn't thought of that but it certainly makes sense.

That said, I think we may be referring to different things when we use the word "property". Stroustroup is talking about properties in the mathematical sense, i.e. he's talking about the properties of the operation "draw()" in the concept Drawable.

Am I misunderstanding you? To my mind that's an operation and closer to "message" (emphasis on the verb aspect) rather than data or property.

Re:Is Go Duck-Typed? by loopz on Wednesday March 04, 2020 @04:32PM (#22488381)
"Drawable" as a name is pretty bland, like patterns. Do we ever really say "drawable" out loud?

So I suspect if we seek more meaningful objects and models, they become more active, have better names, improved design, etc.

If you ask a janitor about Drawable, will he say it's all about draw()? So I think it's kind of a dreary default that we need to avoid and focus on who the actors are, what messages they will pass, and the hidden treasures they encapsulate (don't show, but tell!).

Re:Is Go Duck-Typed? by throwaway894345 on Wednesday March 04, 2020 @04:23PM (#22488295)
Frequently in Go I find that single-method interfaces are easier to work with when made into a closure. Instead of `type Foo interface { Foo() }`, I often find `type Foo func()` easier to work with. There are probably exceptions to this; I'm mostly just voicing my own observation.
Re:Is Go Duck-Typed? by steveklabnik on Wednesday March 04, 2020 @07:33PM (#22489780)
Neg is used to implement operator overloading for unary minus; it's unclear how you would implement this functionality without only doing one thing.
Re:Is Go Duck-Typed? by seertaak on Thursday March 05, 2020 @12:20AM (#22491168)
Thanks for clarifying that; I was actually aware of the link to syntactic sugar and it's probably true that you want to control that independently. After all, who doesn't want to hijack unary minus for the purpose of as hackneyed rendering of EBNF? C++ certainly does it :)

Anyway a serious answer to your comment would be to create a field or ring concept. This would unify some or all of the arithmetic operations.

A neat bonus would be the ability to overload the one and zero literals for the set. So in field of nxn matrices on real numbers 0 is the zero matrix and 1 is the identity.

Re:Is Go Duck-Typed? by steveklabnik on Thursday March 05, 2020 @10:10AM (#22494106)
> After all, who doesn't want to hijack unary minus for the purpose of as hackneyed rendering of EBNF?

Ha!

> Anyway a serious answer to your comment would be to create a field or ring concept.

Yeah, this is true. We really, really struggled to create a set of traits for numerics, even regardless of these more advanced ideas... so it's unclear this would work. But it's certainly closer than I had thought :)

Re:Is Go Duck-Typed? by mjr00 on Wednesday March 04, 2020 @02:57PM (#22487387)
In theory, yeah... in practice, as someone who uses Go and Python with mypy (which supports structural type-checking using the Protocol interface) professionally, I haven't seen it happen. It's easy to think of contrived examples like the one in the post, where Cat/Dog/Table all implement GetLegCount(), but in practice your interface methods usually aren't that simple, and interface signatures like

  func InsertFoo(*sqlx.DB, Foo) (int64, error)
are very unlikely to be unintentionally implemented.
Re:Is Go Duck-Typed? by mrkeen on Wednesday March 04, 2020 @03:02PM (#22487439)
I tend to use nominal types a lot in other languages, so I have a lot of types with id() or value().

Would those be mixed up?

Re:Is Go Duck-Typed? by zaphar on Wednesday March 04, 2020 @07:23PM (#22489720)
In practice if your 'id() string' method returns an id as a string for the object doesn't it do what you need it to? So you actually need to care whether the intended to satisfy your contract or not?

You might care if you expect the Id string to look a particular way. But then you probably wanted to use a type other than string for the return type.

Re:Is Go Duck-Typed? by mrkeen on Saturday March 07, 2020 @11:21AM (#22512324)
> you probably wanted to use a type other than string for the return type.

That's the point. In order for (String userId) to not be confused with (String password), I wrap them with nominal types UserId and Password.

If the language looks at both UserId and Password and decides they're interchangeable because they both satisfy the contract 'contain a String', then that completely negates the point.

Re:Is Go Duck-Typed? by morelisp on Wednesday March 04, 2020 @04:35PM (#22488408)
They would be, but it's also unlikely you would be able to write something useful using only the id and value such that you might confuse something.

There's some "self-limiting" - the smaller the interface, the more likely you get accidental implementation, but also the less likely you are to confuse them in a way that typechecks but actually does something unexpected.

A practical example might be io.Closer - I am certain I implemented this in a dozen types I never intend to pass to any io functions. But no io functions actually take just a Closer, and if I did write some generic cleanup routine that only requires a Closer, it's probably fine and maybe even good if it works on byte IO and gRPC connections and Kafka streams. (Especially without generics, it might be the best I can do.)

Re:Is Go Duck-Typed? by masklinn on Wednesday March 04, 2020 @03:44PM (#22487865)
If the interfaces have only id/value with matching parameters and return types then yes the interfaces would be functionally identical and all objects satisfying one of the interfaces would satisfy the others.
Re:Is Go Duck-Typed? by Nullabillity on Wednesday March 04, 2020 @03:04PM (#22487464)
And at that point, you have.. nominal types with less documentation?
Re:Is Go Duck-Typed? by superlopuh on Wednesday March 04, 2020 @10:47PM (#22490783)
Anecdotally, this happened to me today
Re:Is Go Duck-Typed? by apta on Wednesday March 04, 2020 @05:29PM (#22488857)
It has already caused several issues in golang, and this is an example: https://github.com/golang/go/issues/16474
Re:Is Go Duck-Typed? by masklinn on Thursday March 05, 2020 @03:24AM (#22491874)
That’s a different issue. This is not just using structural interfaces but downcasting to even lower interfaces without warning. That is, the method lies about what it’s looking for.
Re:Is Go Duck-Typed? by apta on Saturday March 07, 2020 @01:58PM (#22513304)
It will cause issues if a type implements the interface where it didn't mean to.