News for hackers. Stuff that matters.
Re:Eight years of Go by krylon on Friday November 10, 2017 @01:50PM (#15671984)
You are not wrong.

But I think there is more to it. I have used Go (almost) exclusively for private toy programs I write in my free time to relax (sounds weird, I know), so my perspective may be warped. But something about is very compatible with the way my mind works. With some other languages, say C or C#, I find myself constantly browsing through documentation to figure out what a given construct means in that language. And don't get me wrong, I like both of these languages.

But with Go, my intuition what I think a given piece of code should mean is nearly always in line with the language specification. The only other language I had this level of rapport with is Python.

There are many things I miss in Go, but all in all, I think it is simplicity done right.

BUT if Go had not also gotten the things right that you mention, it probably would not have become this popular.

Re:Eight years of Go by tetraodonpuffer on Friday November 10, 2017 @02:26PM (#15672261)
> my intuition what I think a given piece of code should mean is nearly always in line with the language specification.

for some things yes, but for others I think that it's more familiarity than intuition, take for example interface slices, you start by learning that you can assign any type to interface{}, so intuitively you'd think that you could assign any type slice to []interface{} but you find out soon enough that doesn't work

If you dig a bit it is completely understandable why it doesn't given how interfaces are laid out in memory, but from an intuitive standpoint you'd think that since you can do it in the scalar case you should be able to do it in the slice case too...

This is a very minor nitpick, I do agree that compared to many other languages golang is very easy to mentally parse, at the expense of some expressivity at times.

Whenever I code in it, which these days is pretty much all the time, I do miss some conveniences from other languages (generics, list comprehensions, ...) but of course every language has its tradeoffs, I most certainly miss golang's strengths every time I have to write some C or python.

Re:Eight years of Go by terminalcommand on Friday November 10, 2017 @02:46PM (#15672380)
IMO interfaces are the Achilles' heel of Golang. Another thing that has been bugging me about them is that you cannot specify which interface you're implementing in the code (beside from comments). I think that is so because Go hates circular imports. If you had to import a file to be able to use an interface, you'd soon run into compile errors due to circular dependencies.

And as there is no way to immediately see where some interface is declared and if/which interface is implemented, you have to rely on comments and resort to manual search to find out more. That may be easy in smaller codebases or worthy of effort in important codebases such as Go standard library. But all an all this ambiguity does not fit with Go's discipline to prevent human errors by making everything uniform and explicit as possible.

Just for the sake of providing an example, let's look at time.go under https://golang.org/src/time/time.go, we can see in the comments of several functions that the programmer states a particular interface is implemented. Line 1112 of the aforementioned file is as follows:

  // MarshalBinary implements the encoding.BinaryMarshaler   interface.
  func (t Time) MarshalBinary() ([]byte, error) {
Now where is encoding.BinaryMarshaler declared? There is a package called encoding, maybe there? But the package has many many files, where would I find where BinaryMarshaler is defined? I'd have to resort to manual search. Now imagine that this is not an interface in the standard library, but rather an interface in some mediocre codebase that you're handed for the first time...

Tl;dr All I'm saying is that runtime errors due to empty interfaces are not the only flaw of Golang. Interfaces as implemented in Go could in some cases present a serious threat to code structure.

Re:Eight years of Go by cube2222 on Friday November 10, 2017 @03:39PM (#15672786)
I actually experienced the opposite. Interfaces in go work so well, partly because you can create an interface which automatically gets satisfied by existing structures. For example I often use a requestDoer interface which only has the http clients "Do" method.

I'm using Goland so I have jump to interface from any structure implementing it. So that's another one that gets solved by tooling.

The structurally typed interfaces are actually my favorite part about Go probably.

Re:Eight years of Go by terminalcommand on Friday November 10, 2017 @03:59PM (#15672984)
How do you jump to the interface from any structure implementing it? Beside from comments there is no immediate way to which interface/interfaces a structure is implementing.

Maybe Gogland solves this by keeping records on all interfaces, and checking all structures whether they satisfy them. If that's the case, this is definitely solved by tooling.

Re:Eight years of Go by sythe2o0 on Friday November 10, 2017 @04:15PM (#15673108)
If you need this, you can write type assertion lines in a var block of the interfaces that you explicitly want to implement (and this will throw compile time errors if they don't).
Re:Eight years of Go by slrz on Friday November 10, 2017 @05:27PM (#15673598)
The standard godoc documentation browser does this too when started with the -analysis option. They all use the same source code oracle functionality available from the x/tools repo.
Re:Eight years of Go by cube2222 on Friday November 10, 2017 @06:51PM (#15674089)
No, Goland actually doesn't use that, they're rolling their own for everything.
Re:Eight years of Go by slrz on Saturday November 11, 2017 @03:52PM (#15678125)
Thanks for the correction. I think that's somewhat unfortunate, though. The powerful shared tooling is one of the strengths of the Go ecosystem and if there are issues with it that prevent the use in Goland I'd rather see those get fixed.

OTOH, they (Jetbrains) probably have shared infrastructure (between languages) within their IDE code base, too, and are more comfortable using their existing tooling.

Re:Eight years of Go by cube2222 on Saturday November 11, 2017 @06:12PM (#15678720)
They are actually parsing the code into their custom ast format, so they have a lot of things done out of the box already.
Re:Eight years of Go by cube2222 on Friday November 10, 2017 @04:14PM (#15673100)
That's exactly what it does as far as I know.

EDIT: I can also search for any interface with a struct selected, and generated stubs for all method. Well, basically what you get for any other language when implementing interfaces.

Re:Eight years of Go by krylon on Friday November 10, 2017 @02:56PM (#15672445)
It would be nice to add at least the options to explicitly state that type X should implement interface I, and have the compiler barf / emit an error if X does not implement Y.

Like I said before, this has not been enough of a problem to really bother me, but I would be quite happy if Go fixed this.

Re:Eight years of Go by tetraodonpuffer on Friday November 10, 2017 @03:00PM (#15672486)
can't you simply do this (from [1]) somewhere in your code to enforce?

    type T struct{}
    var _ I = T{}       // Verify that T implements I.
    var _ I = (*T)(nil) // Verify that *T implements I.
[1] https://golang.org/doc/faq#guarantee_satisfies_interface
Re:Eight years of Go by terminalcommand on Friday November 10, 2017 @03:12PM (#15672569)
Yes you could, but then you'd have to have both the declaration and the implementation of the interface in the same package. That would mean that if either of them is in a separate file, you'd have to import it. That in return, could easily call hellish compiler errors due to circular dependencies. In Go any circular dependency is an immediate compile error.

Looking at the link, the second advice is that you could require users to implement special functions such as ImplementsFooer() to explicitly declare which interface they're implementing. This could help, but this practice is not present in the codebases I've seen including the Go standard library (to the best of my knowledge).

Re:Eight years of Go by majewsky on Friday November 10, 2017 @03:19PM (#15672619)
A possible solution is to have a separate (otherwise unused) package in your codebase with just a test in it:

  //compile_test.go
  func TestThatThisModuleCompiles(t* testing.T) {}

  //list of "type implements interface" tests
  var _ mymodule.MyInterface = myothermodule.MyStruct{}
  ...
Then you just ensure that `go test` is run, e.g. by the CI.
Re:Eight years of Go by terminalcommand on Friday November 10, 2017 @03:41PM (#15672805)
That's a clever way to solve the issue. I think most Go codebases could benefit from such explicit checks where the language does not provide an unambiguous solution.

The interface issue has been on my mind for quite sometime. I had thought maybe declaring all interfaces in seperate files might help with the structure (like having the definitions in a header file in C), but the Go standard library does not use this approach. Interfaces and implementations are scattered across files and modules. Maybe I'm being too clever and we all know that Rob Pike always says that programmers should not be too clever. Maybe I should loosen up a bit, and treat Golang more like a pragmatic language. They did the best they could and produced a wonderful language. Any additional feature such as generics and explicit implements statements would have made the language much more complex.

Re:Eight years of Go by infogulch on Friday November 10, 2017 @06:11PM (#15673883)
This can go even further.

All the go files in a directory have to declare the same package name (`package mypkg`), with one exception: a test file (filename ends with `_test.go`) that has a package name that's the same as the normal package but ends with `_test` (as in `package mypkg_test`) is allowed to be in the same directory.

These are actually completely separate packages and you have to import the main package if you want to actually use it. But when you run `go test` it automatically complies and runs tests in both packages. This means that you can import both your package and a dependent package to make sure interfaces are satisfied without any circular dependencies.

Re:Eight years of Go by cube2222 on Friday November 10, 2017 @03:53PM (#15672937)
Sibling comment shows one way to do it, but in most cases, at least for me, which is when both the struct and interface are in the same package it gets solved by the NewMyInterface method.

As it's return type is MyInterface, but it actually returns myStructure which causes the compiler to check the interface satisfying.

Re:Eight years of Go by dansouzaio on Friday November 10, 2017 @08:33PM (#15674497)
> And as there is no way to immediately see where some interface is declared and if/which interface is implemented

you can use go-oracle (https://camo.githubusercontent.com/3fb1d62bbc7b1306da783f395... that!

Re:Eight years of Go by geoka9 on Saturday November 11, 2017 @02:33AM (#15675365)
Re:Eight years of Go by dualogy on Friday November 10, 2017 @07:14PM (#15674200)
> I think that is so because Go hates circular imports.

Well it could be but I don't think so --- "duck typing" when it comes to interface{}s has been a goal from early on, quite simply. The very idea of interfaces as "a set of func signatures" preclude the necessity to verbosely spell out "struct Foo implements BinaryMarshaler" and/or "func (_ Foo) MarshalBinary implements BinaryMarshaler.MarshalBinary"!

In the same vain, you don't even need to declare interface TYPES, you can accept/pass/return "inlined" interface{MethodSig(argtype)rettype} anywhere if you want. Not the most ergonomic choice in practice, but the possibility hints at this underlying interfaces-are-duck-typed-method-sets paradigm at work here.

Re:Eight years of Go by slrz on Friday November 10, 2017 @04:58PM (#15673394)
> take for example interface slices, you start by learning that you can assign any type to interface{}, so intuitively you'd think that you could assign any type slice to []interface{} but you find out soon enough that doesn't work

Why do you expect this to be the case? Just because you can assign a char to an int in C, you wouldn't expect to be able to assign a char pointer to an int pointer. Similar for C++ and std::vector<char> vs std::vector<int>.

It's the same and probably even more relevant with void∗/int∗ (assignable) vs. void∗∗/int∗∗ (not assignable). You can assign an int∗∗ to a void∗, though, just as you can assign an []int to an interface{} in Go.

Also, even as an absolute greenfield programmer with no experience in other system languages, if you really think it through, you will come to the conclusion that it mustn't be possible to assign arbitrary slice values to an []interface{} variable, given the way slices work.

Re:Eight years of Go by donatj on Friday November 10, 2017 @04:34PM (#15673254)
> intuitively you'd think that you could assign any type slice to []interface{} but you find out soon enough that doesn't work

I've been writing Go for upwards of six years, professionally for three, and I have never tried that. I can't think of the last time I used interface{}...

I generally consider anything using interface{} crap code someone who doesn't know the language wrote, probably coming from a loosely typed language.

Re:Eight years of Go by dualogy on Friday November 10, 2017 @07:19PM (#15674217)
> I generally consider anything using interface{} crap code someone who doesn't know the language wrote, probably coming from a loosely typed language.

Disagree. There's ample usage of interface{} that you need to type-switch on in parts of the stdlib that deal with the AST and reflection or various other areas. You might also UnmarshalJSON a structure where some field can contain any one of a number of possible sub-structures. Once we're talking about 1-2 dozens of possible sub-structures, you'll have that field be an interface{} and type-switch on it --- rather than having 1-2 dozen pointer fields that will all-but-1 be nil and having to if-else through them all.

Re:Eight years of Go by nine_k on Friday November 10, 2017 @01:58PM (#15672049)
Go does have brilliant simplicity! It's the simplicity of Pascal I used in middle school, and of Modula-2 I used on my freshman year, with basically the same syntax. I'm glad Go revived a number of good ideas from Pascal / Modula / Oberon.

For writing toy programs to relax (can relate), I personally prefer Python, or maybe a Scheme. While also being simple at the core concepts, they have much more expressive power, an easier way to combine simple things into complex things. They pay for that by higher resource consumption, of course. I do fondly remember a PDP-11 Pascal compiler reporting "15 KB used" after compiling my program; you can't get a Python process with these constraints. But by now we have _vastly_ more computing power.

Re:Eight years of Go by TheOtherHobbes on Friday November 10, 2017 @02:55PM (#15672444)
Go has human-sized simplicity. Programming language complexity is limited by human cognitive limitations, and Go has a small cognitive load.

This makes it feel like a very intuitive language.

That doesn't mean it is an intuitive language - just that it feels like one for a specific class of problems.

Personally I enjoy using it for the same reasons as everyone else. But... I'm also aware it's quite an old-fashioned language, with some bumps under the floorboards where useful things were hammered down to make them go away, instead of being fully solved.

Re:Eight years of Go by krylon on Friday November 10, 2017 @05:43PM (#15673704)
There is a good chance you are right.

But as long as it is humans writing most/all Go code, fitting a human brain well is a valid design choice.

Yes, it has its fair share of problems. But for now, I find it preferable to anything else we have got.

Re:Eight years of Go by pjmlp on Friday November 10, 2017 @04:27PM (#15673199)
> I'm glad Go revived a number of good ideas from Pascal / Modula / Oberon.

Sadly it feels more like Oberon-07 than Modula-2, feature wise.

Re:Eight years of Go by johndubchak on Friday November 10, 2017 @03:25PM (#15672677)
> I write in my free time to relax (sounds weird, I know)

Not weird at all. I do that myself. I believe the relaxation is in pure creativity, the wonder of exploring something novel and the absence of any pressure or stress due to timelines or deadlines. You're also exercising both right and left sides of the brain.

I suspect there are many many more who do the same.

Re:Eight years of Go by krylon on Friday November 10, 2017 @03:41PM (#15672800)
Huh. That are exactly the reasons I would give if I had to explain that hobby to somebody. ;-)

Feels kind of good to know that I am not the only one doing this.

When it goes really well, programming is akin to what I think meditation must be like. But explaining that to people who have never programmed themselves is hard.

Re:Eight years of Go by mioelnir on Friday November 10, 2017 @07:25PM (#15674242)
> But something about is very compatible with the way my mind works.

Yes. For me it is channels. After nearly 20 years of UNIX'ish systems, pipes are a mental abstraction that I do not have to think about any more. And channels fit right in, they feel much closer to a how a pipe is used on the cli than a pipe or socketpair ever did in code.

For example a range loop over a closed channel is, for me, piping things to xargs. It's easy to understand, reason and conceptualize because it feels familiar.

Re:Eight years of Go by insulanian on Friday November 10, 2017 @03:48PM (#15672884)
> sounds weird, I know

No, it doesn't.

Re:Eight years of Go by krylon on Friday November 10, 2017 @06:03PM (#15673825)
That is a relief.

I know other people meet a similar purose by knitting or solving crossword puzzles, but explaining to them the trancendetal nature of their favorite pastime is quite difficult.