Your two paragraphs give example to the exact problem. “It doesn’t matter what the name is, interfaces aren’t precious”, and “you don’t need to see the implementation to know what it does, just read the method name and type signature” right?
Not sure how you hold those two things in your head at the same time, but they are anathema to each other. Different implementations of the same function name and type signature can have drastically different effects in go because side effects are everywhere in go, so you must read the implementation to understand what it does.
If this was Haskell and I could read the type signature and trust that I know what the system did with that (ignoring Rich Hickey’s point about the type signature not describing what “reverse” does) then fine, but in every function call there are unconstrained numbers of side effects, go functions which persist after the function goes out of scope and can modify any pointed to memory at any arbitrary time later… go is the Wild West in this regard. The interface method name + weak go type system function definition is not enough to tell a developer what the implementation of that interface actually does. Finally: Java’s “implements” plus excellent IDE support for Java DI allows a developer to jump to the implementation in one keyboard press, this does not exist in go. You’ll probably never know what method is actually called unless it’s runtime with the actual config on the server.
I’m not going to explain the whole reasoned argument about why it’s important for a programmer to understand program execution flow in their head clearly, Dijkstra did a much better job than I ever could with GOTO considered harmful, but check out a modern descendant of this article specifically talking about go functions, and try to internalize the point about being able to understand program execution flow:
I see a few of my words and others that I neither said nor thought. With respect, perhaps you're rushing in a particular unrelated tangent and drawing conclusions.
The points I was trying to draw your attention to was that duck-typing as it's done in Go (structural typing to be more exact), is at the crux of its approach to interfaces. Do you understand duck typing or structural typing?
To summarize what I've already tried to say before, Go interfaces are not Java interfaces. Java cares about the interface as a type, while Go cares only about the listed methods (the behavior). These are two completely different paradigms, they don't mix so well, as former Java programmers doing Go are discovering. In Java, interfaces themselves are important because they're treated as types and programmers tend to be economical about them. They're used everywhere to say that a class implements them and that they're a dependency of some consumer. In Go the interface is just a convenient but unimportant label that points to a list of methods that a consumer expects a particular dependency to have. Only the list of methods is meaningful. The label isn't. That's it. Done.
Again, completely different paradigms. If you embrace Go interfaces, the way you read, write and think about Go code also changes. But complaining about them with a Java mindset is complaining that a wrench is a bad screwdriver.
At the end of the day, it's up to you to decide whether you can open your mind and actually learn to use a tool as it was meant, or just assume that its creator and all the people that claim to do so successfully are in denial for not admitting to share the pains you have.
Look, I understand duck typing. Go doesn’t have it, the fact you keep calling go’s dynamic dispatch duck typing is a bit of a red flag, but I can see why you would think that. It’s not my point though, you’re missing what I’m saying.
You’re saying, essentially, that you just use the objects method and you don’t need to read its implementation to understand what it does, if something has a “Update” method and it takes a new copy of the object and it’s a pointer method, as a caller we can assume that we give the new data to that update method and it’ll just take that data and patch it into the object at that pointer address. You don’t have to read the method and go on with your day, the interface proves it works and you don’t need “implements”
The problem with this is there are bad programmers who do crazy things with “Update”. Some people will kick off go routines that do things later and mutate things you don’t expect. So when you fetch 500 things then update them in a loop and suddenly nuke your database with 50,000 writes simultaneously that are all contending for the same key, you will go back to Update and see… oh fuck this update method uses some clever Postgres queue and keeps a lot of metrics that are also in Postgres, that’s why my system locked when it shouldn’t have. I should have read this Update method.
So that is crux of my point. Having the method name and function parameters only is not enough to understand your program, and using single interface definitions all over the place hurts readability and understanding.
> You’re saying, essentially, that you just use the objects method and you don’t need to read its implementation to understand what it does
Your recent points have nothing to do with mine. What you're thinking that I'm saying is not what I'm saying. I'm still very much aligned with the original topic of the article, Interface Segregation as it's done in Go. An article to which you reacted adversely, while demonstrating that you're clearly still looking for Java where it doesn't exist. I'll leave things at that, since I'd just be repeating myself at this point.
I’m not a Java dev, my preferred languages are Clojure, scala, rust, and python. I think you assumed ignorance, tried to explain something your colleagues probably don’t get to someone who understands it, and then haven’t put the effort into reading the link I sent earlier, and I’ve failed miserably at explaining my point.
I think you’d have to have seen the problem, and maybe it only pops up in a large go monorepo that uses DI the IDE can’t see through, while simultaneously having to work with interfaces like “Get” where some of the implementation was written by devs who think Get is a mutation.
You’re right though. We’re talking past each other and both think the other one is a moron. Leave it to the reader to figure out which one is I suppose.
Not sure how you hold those two things in your head at the same time, but they are anathema to each other. Different implementations of the same function name and type signature can have drastically different effects in go because side effects are everywhere in go, so you must read the implementation to understand what it does.
If this was Haskell and I could read the type signature and trust that I know what the system did with that (ignoring Rich Hickey’s point about the type signature not describing what “reverse” does) then fine, but in every function call there are unconstrained numbers of side effects, go functions which persist after the function goes out of scope and can modify any pointed to memory at any arbitrary time later… go is the Wild West in this regard. The interface method name + weak go type system function definition is not enough to tell a developer what the implementation of that interface actually does. Finally: Java’s “implements” plus excellent IDE support for Java DI allows a developer to jump to the implementation in one keyboard press, this does not exist in go. You’ll probably never know what method is actually called unless it’s runtime with the actual config on the server.
I’m not going to explain the whole reasoned argument about why it’s important for a programmer to understand program execution flow in their head clearly, Dijkstra did a much better job than I ever could with GOTO considered harmful, but check out a modern descendant of this article specifically talking about go functions, and try to internalize the point about being able to understand program execution flow:
https://vorpus.org/blog/notes-on-structured-concurrency-or-g...