re: Go Outta Here VIEW POST

FULL DISCUSSION
 

Oh boy, is this a fun post to read. I'm by no means a professional Go programmer, but I have a done a handful of side projects in the language over the past 3-4 years. I am also part of the initiative at my company to adopt it as one of the 3 standard languages used throughout the company.

Much of your examples show a very base level understanding of the language. This would make sense as you claim to be going through the tutorial.

OK, everybody calls it module, we’d call it package, because we are special. OK.

You don't like the work package? Boohoo. It's synonymous with the word module in that it denotes a group of code. Are you really going to complain about the keyword choice?

import "fmt"? You gotta be kidding me;...

Yes, you have to import things. Just like virtually every other language. The Go standard library is treated the same as all other packages, more or less, so in order for the compiler to #include that code it must be imported. This is a non-issue as it's either very obvious what's wrong, or it's handled automatically by even barebones editors.

Further, the act of importing something can also change how certain packages behave, as evident by the database/sql package and it's various 3rd party drivers:

import (
    "database/sql"

    _ "github.com/lib/pq"
)

By importing pq a function is called internally to register it in the sql package as a driver, allowing Postgres to be used via the standard interface defined in database/sql. I can understand not liking 'magic functionality' here, but it's really a construct done by the package itself, not the language.

The above makes me cry. In the second decade of XXI century we enforce the developer to distinguish between int32 and int64 in compiled “statically typed” language. Really?

The vast majority of people use either int or int64 when explicitly necessary. The rest of the types are there as edge cases for specific optimizations and are largely based off of their C counterparts. Go has fairly close ties to C btw.

interface{} is understandably counter to the static type argument. Which is why you aren't supposed to use it unless it's necessary.

If you don't like static typing or don't think it has value, more power to you. I personally think it leads to better understanding and more resilient code. Go isn't your language. Next.

What could go wrong with a plain old good equal sign?

Without ripping on Python too hard, a lot. Assignment and declaration should be distinct operations. Someone glancing at the code should know where that variable was first set. The := syntax is shorthand anyway, you don't have to use it.

Control structures

for in go functions as a for and while loop in Java, and can be combined with range for iterating over key/value pairs. It's simple, and there's only one way to do it. What's there to complain about? The lack of a post-check loop is about the only thing I can see missing. Go isn't a functional language, but map/reduce can be achieved using anonymous functions pretty easily.

It's also worth noting that Go was/is(?) primarily designed as a systems and cloud-oriented web service language. It's not designed for machine learning, research, analytics, or anything of the like so it's not unexpected that it does not prioritize constructs that are mostly useful in those areas.

maps are to be both declared and initialized, otherwise a runtime error raises (if this is not a shallow copy of the one billion dollar mistake, I don’t know what is.)

var x map[string]int = make(map[string]int)

Nobody initializes maps this way. If you're initializing a map at the same time as declaring it, use the := operator:

x := make(map[string]int)

That's quite a bit better, eh?

if name, ok := elements["Un"]; ok {
  fmt.Println(name, ok)
}

This is because Go doesn't have exceptions, another design decision. This is only something you need to do if you aren't sure if the key is in the map. It's also the shorthand-ish way of writing that:

name, ok := elements["Un"]
if !ok {
  //Oh noes, my value wasn't in the map
  ...
}

It's also worth noting that the value (name) in this case will actually be the default initialized value of that type, 0 in this case if the key was not present.

Maps are usually to be avoided in Go anyway. In most cases, a struct is better suited.

Functions can return multiple values (what’s wrong with returning arrays btw?)

Arrays are all of one type. How often are you returning multiple values of the same type?

Interfaces. Sounds scary?—That’s not all. Void Interfaces.

func Println(a ...interface{}) (n int, err error)

Statically typed? Safe? ROFL.

interface{} is strongly discouraged from general use. In order to do anything with a variable of type interface{} you must make a type assertion on it, and assign it to a new variable. There's big debate over the inclusion of generics in Go, but if they were to be included it would likely fix this problem.

Edit: Interface composition is also one of the best constructs I've ever used in programming. No OO/inheritance BS. Just plain composition and implicit implementation. It's wonderful!

blob of definitions in Elixir

At a glance, Elixir looks like a very aesthetically unpleasing language to look at.

My general opinion on programming languages is they should be restrictive/opinionated enough to force even inexperienced developers to write understandable and functional code, while being flexible enough to not be restrictive to those who know what they're doing. Coming from Java and C# to Python as my current day job language, Go strikes a sweet spot for these types of things IMO. There's a reason it's been adopted by huge portions of the Linux community for tooling and DevOps. Most of Docker, LXD, Kubernetes, and many other cloud-based project are in Go.

I also have a preference towards slightly more verbose code. Again, I think Go strikes a good balance with that. A lot of language choice boils down to use case and personal preference. Who cares what you use as long as it works and is maintainable.

Did someone say system runtime?

 

The whole original post was about Go design is silly and all your objections are that’s by design. That’s smart because that statement immediately puts me out of further arguments.

Elixir looks like a very aesthetically unpleasing language to look at.

We surely have a very different sense of beauty.

 

I wasn't just saying it's "by design", I gave reasons why that design might be desirable. If you're referring to my lack of followup on the key checking in Go, it's because the followup is it's functionally no different than wrapping it in a try/except/catch/whatever and dealing with it then. Tell me, how does Elixir handle a missing key?

We surely have a very different sense of beauty.

Clearly.

Go is pretty darn readable, even for someone who's never written a line of code in it before. The same cannot be said for Elixir. What the hell is %{} or [_|_] supposed to mean?

The whole original post was about Go design is silly and all your objections are that’s by design. That’s smart because that statement immediately puts me out of further arguments.

Someone with an argument would ask questions to lead into their argument, no? Question my assertions. Poke holes in my arguments. I don't write this shit because I want other people to agree with me. I write as a way to understand my own thoughts more and learn more in the process.

Tell me, how does Elixir handle a missing key?

Besides syntactic sugar, the core ways are: Map.fetch/2 to return nil, Map.fetch!/2 to raise and Map.get/3 to return a default value.

Go is pretty darn readable, even for someone who's never written a line of code in it before.

I hear this kōan from almost everybody who likes Go and I am most likely an exception. I can read and understand more than 20 languages, I can write the code professionally in more than 10. 4–5 on senior level. I still having difficulties reading Go, despite I already wrote 1000+ LOCs in it.

How is map[string]int pretty darn readable?

Besides syntactic sugar, the core ways are: Map.fetch/2 to return nil, Map.fetch!/2 to raise and Map.get/3 to return a default value.

Similar(ish) to Python, I agree this is a better way to handle in general. This is a spot where static typing (and not generics) hurts a bit, as you can't cleanly define an interface/method that can return values of arbitrary types without using interface{}. When you declare a map, it's full structure becomes a type. Meaning map[int]string is a different type from map[string]string. I found a pretty good explanation of what's actually going on with Go's maps. What's funny is the standard lib has list and heap implementations that use interface{}, and I'm not sure why they don't also have a dict package that does the same thing. Here, I just did it.

kōan

Sorry, I'm not sure what language this is so I can only guess that it means something like "banal" in English.

I can read and understand more than 20 languages, I can write the code professionally in more than 10. 4–5 on senior level. I still having difficulties reading Go, despite I already wrote 1000+ LOCs in it.

All those languages knockin' around in your head must be confusing you 😉

I would stick with it and write a few small web applications in it, or don't. It's hardly the best language out there, I just happen to like it a lot.

How is map[string]int pretty darn readable?

I'll agree it's one of the uglier constructs in go. Again, it's worth calling out that I've used maps maybe 2 or 3 times in the 3+ years I've been programming in Go. structs tend to get used more often than in dynamic languages (Python dict hell!!)

 

Arrays are all of one type. How often are you returning multiple values of the same type?

type ReturnValue [2]interface{}

code of conduct - report abuse