1. Synchronous by default 2. Interfaces
Point 1 to me feels silly to admit. But man, i still do a fair bit of JavaScript/Node for work (our frontend, mainly) and it is just so.. tiresome to me, to constantly feel on the edge of bugs because some functions are expected to be synchronous (return values) and others are intended to callback.
I'm not even talking "nested-hell", i'm simply referring to the mental overhead that i apparently use when using any JavaScript function. It's not difficult.. it's just not enjoyable. It's.. tiresome for me.
Point 2 is simply because i really like interfaces. Back from my Python days, a few libraries had invented ways to support interfaces and i really really liked them, but not the overhead they required. Being able to expect the behavior of an object with certainty is the main part of duck-typing to me, and seeing that from a more static language (when compared to python/js) is really enjoyable.
I know you didn't ask for a writeup, i just had to agree with you and felt the need to share my experience from a similar standpoint. :)
Pretty much why i never got into node development.
One approach I've been playing with in JavaScript-land is using Promises for all return values, this means that you consistently know how to interact with a return value, whether its concrete-value is available immediately or at a later time -- this doesn't really solve the problem of consuming other people's libraries however!
They seem rather harmless to me, so i'm curious on your perspective. I see them as no more dangerous than an explicit type (or any function, for that matter).
You're asking for a specific behavior, and you know that what you get will have that specific behavior. A Reader or Writer is a simple example of that. Sure, you don't know what it's writing to, but that's outside your scope and likely the scope of the function - right?
Perhaps i misunderstand you, i'd love further explanation/examples _(pertaining to Go's interfaces)_ :)
How am I supposed to switch between types being able to do either of these, without listing all types that might occur – as this might not be possible
(I’m thinking about Haskell’s type classes as a solution to this, btw)
Basically, you don't do that in Go. Go has interfaces, but they're implemented implicitly. So rather than "I get an object", you say, "I get an object that implements interface X", and you use whatever methods X has.
That just means its up to you to name interfaces in a reasonable way. Which is one down side - if you have an "Add" method, it's back to your question about what it means to add, depending on the implementation.
Nothing does this as well as Haskell as far as I can tell.
So in your example, you might have an Adder interface that simply checks for the Add() method, but if you know you're going to need VectorAddition, Addition, and Appendable, make a new interface that implements all three, then use that.
You can do this in one large interface, or 3 small interfaces that you use to compose a 4th, larger interface (usually the preferred way). Example:
type Adder interface {
Add()
}
type VectorAdder interface {
VectorAdd()
}
type Appender interface {
Append()
}
type VectAddAppendable interface {
Adder
VectorAdder
Appender
}
Make sense? Forgive any lapses in syntax, it's been a long day. But i think this is the solution you were referring to correct? By saying that the object coming in must implement the VectAddAppendable interface, you can be positive that it implements the methods you care about.A good example of this is in Golang's stdlib IO package. There's Reader, Writer, and ReadWriter. ReadWriter simply requires both the Reader and Writer interfaces. It's a very common paradigm.
Does that solve your issue?