Hacker News new | ask | show | jobs
by dllthomas 4241 days ago
Right, thank you!

Traditional (but even more contrived) examples include "draw" and "fire" doing unexpected things when applied to a gun as opposed to an image or an employee.

1 comments

ok, ok. I think I'm understanding. Let me propose a middle ground.

First, you really have to split this up into two distinct cases.

The most powerful/useful is when you create an interface in your package for a structure in a different package. It's been years since I've done .NET or Java, but I remember frequently cursing a library developer (often someone at Microsoft or Sun) for exposing a concrete type instead of an interface. In this case, I don't see any downside. Creating a:

type Render interface { Draw() }

And then using my (or someone else's) Gun structure, is no different than using the Gun structure directly and expecting it to render itself to the screen rather than come out of its holster. The interface buys you the level of indirection, but whether you use the interface or the concrete type, it's up to you to know WTF Draw() does.

This first approach is well explained at [1]

In the second case, where a library expects an interface, I agree there's a risk:

// 3rd party package package fastRender

type Drawable interface { Draw() }

func Render(d Drawable) { ... }

// my code: fastRender.Render(new(Gun))

I've lost some degree or type checking. Since my Gun's Draw doesn't explicitly implement fastRender.Drawable, maybe I'm trying to fit a square peg into a round hole. There's nothing the compiler can do to help either.

It sounds a bit like an academic concern to me. Is this something that's actually bitten people in the ass? How is it different than any method that takes primitives? I can pass a []byte of a bitmap to a Music player's Play(data []byte) method...

[1] https://medium.com/@rakyll/interface-pollution-in-go-7d58bcc...

Note that I'm not arguing for Java. Haskell typeclasses covers both of these use cases (you can make a new type an instance of an existing typeclass, or existing types instances of a new typeclass, without touching any exsisting code) but requires someone to look at it and say "This actually implements the contract that interface assumes." They can be wrong about that, of course, but I think making it explicit helps.

There's also a subtle third case - I import two libraries. One of them defines Drawable, the other has something that looks like it implements Drawable. But it's just happened upon the same signature, and makes different assumptions. Maybe it even says it implements Drawable - but a different Drawable defined in a third library.

As I've said all along, I don't know how often these things are hit in practice. I think so far go development has been comparatively centralized, and so it might be seen less often than it would if it were used everywhere, but that might still be rare enough to not be a problem.

thanks