
The Value in Go’s Simplicity - Cthulhu_
https://benjamincongdon.me/blog/2019/11/11/The-Value-in-Gos-Simplicity/
======
dunkelheit
One benefit of radical go-style simplicity that I haven't seen discussed much
is that it forces you to focus on the task at hand. Like, you know that your
code will look like shit anyway so it is pointless to fuss too much over it.
Whereas many programmers using a more "clever" language like haskell will
spend a lot of time trying to make code more readable, searching for the right
abstraction, arguing whether something is a monad or some other gizmo. Most of
it is wasted intellectual effort.

Everything in moderation of course. For me personally simplicity of go is too
much and I don't feel comfortable writing it.

~~~
atombender
I agree that Go forces you to focus on mechanics of your code more so than
some "fancier" languages.

However, Go's poor type system also forces you to write worse and less safe
code than you want.

For example, the lack of enums and sum types means it's hard to represent,
safely and efficiently, anything that should be the same type but have
different values within a strict set of allowed values. The nearest you get in
Go to an enum is:

    
    
      type Color int
      const (
        Red Color = iota
        Blue
        Green
      )
    

Alternatively, can do the same with a string:

    
    
      type Color string
      const Red Color = "red"
      // etc.
    

This gives you _some_ type safety, since a Color requires casting to force an
int or string or whatever to become it. But that doesn't give you real safety,
since you can do:

    
    
      var c Color = Color("fnord")
    

This comes up whenever you're handling foreign input. JSON unmarshaling, for
example. You can override the unmarshalling to add some validation, but it
won't solve your other cases; it will always be possible to accidentally
accept something bad. Not to mention that the "zero value" of such a value is
usually wrong:

    
    
      type Color string
      var c color // Empty string, not valid!
    

A more safe way is to hide the value:

    
    
      type Color interface {
        isColor()
      }
      type colorValue string
      func (colorValue) isColor() {}
      var Red Color = colorValue("red") // etc. 
    

Now nobody can create an invalid value, and the zero value is nil, not an
invalid enum value. But this is unnecessarily complicated and shouldn't be
necessary in a modern language in 2019.

The case of genuine sum types is worse. Your best bet is to use sealed
interfaces:

    
    
      type Action interface {
        isAction()
      }
    
      type TurnLeft struct{}
      func (TurnLeft) isAction() {}
    
      type MoveForward struct{
        Steps int
      }
      func (MoveForward) isAction() {}
    

There are some downsides. You have to use static analysis tools (go-sumtype is
good) to make sure your type switches are exhaustive. You get a performance
penalty from having to wrap all values in an interface. And if you're going to
serialize or deserialize this (e.g. JSON), you will be writing a whole bunch
of logic to read and write such "polymorphic" values.

~~~
strken
Agree with this. There are couple of observations I'd make, though.

Firstly, "enums" using iota should always be defined as either

    
    
        const (
            Red Colour = iota + 1
            Blue
            Green
        )
    

or

    
    
        const (
            Unknown Colour = iota
            Red
            Green
            Blue
        )
    

to avoid the zero value problem.

Secondly, and this is a personal preference, I've really enjoyed not having to
work with sum types. In practice other programmers seem to use them when a
public interface would have been sufficient, and it's convenient to be able to
do:

    
    
        // original package 
        type Position struct {
            X, Y int
            Rot Rotation
        } 
    
        type Action interface {
           Apply(Position) 
        }
    
        type MoveForward struct {
           Steps int
        }
        func (m MoveForward) Apply(p Position) {
            switch p.Rot {
            case Up:
                p.Y += m.Steps
                ... 
            }
        } 
    
        // second package wrapping the first
        type WarpToPoint struct {
            X, Y int
        }
        func (w WarpToPoint) Apply(p movement.Position) {
            p.X, p.Y = w.X, w.Y
        }

~~~
riquito
Enums exist specifically to be compared with one of their possible values, how
can you have a zero value problem?

~~~
gen220
If the caller of your interface does not specify a value for your enum, they
have implicitly specified the zero value. Whether that’s desirable behavior or
not is up to you. For most clients, this behavior can be surprising if the
zero value is a meaningful one (i.e. one that implies an intentional choice).

IME it’s useful to explicitly define the zero value’s meaning as
“UNSPECIFIED”, to simplify the problem of trying to guess if the client
intended to pass a zero value.

------
matthewmacleod
Okay, so there's definitely simplicity. I agree that this is in many ways a
nice change from some of the more popular languages, which can get a bit
complex and heavy, with a focus on features that are nice in isolation but add
up to a surprisingly difficult architecture to comprehend at the global level.
I just don't think Go gets the balance right.

There are some parts of the language that are such a joy to use – C interop is
simple and elegant, the concurrency story is great, the standard library is
great, and in contrast to some other people, I think the error handling is
also a nice example of simple and effective design. The wider tooling system
is decently usable (with the exception of gofmt, as mentioned in the article,
which I think is the single best tool I've ever used in any language).

But "simplicity" in Go-land seems sometimes to be the enemy of
_expressiveness_. The lack of abstractions over operations like filtering,
mapping, or removing items from a collection is incredibly irritating for such
a common operation – instead of immediately obvious actions, there are tedious
manual loops where I end up having to read like 10 lines of code to figure out
"oh this just removes something from a list". The use of zero values is a
crime against nature. The type system is shameful in practice compared to
other modern languages, with _so much code_ just being "just slap an
interface{} in there and we can all pretend this isn't a disaster waiting to
happen". It feels like such a lost opportunity to exploit a richer type system
to eliminate whole classes of common errors (which are predictably the ones I
keep making in Go code.)

I guess it's frustrating that a language—which is relatively well-designed and
intended to be simple—too often makes things _more complicated overall_ by
making fallible humans do things that computers are better and more reliable
at. I'll keep using it, because it fills a really nice niche that no other
language is quite suitable for. But I'll keep being a bit annoyed about it,
too.

~~~
Rotareti
_> C interop is simple and elegant_

C interop in go is super slow: [https://github.com/dyu/ffi-
overhead](https://github.com/dyu/ffi-overhead)

~~~
_ph_
Which isn't a contratiction to the statement, that for the programmer the C
interop is simple and elegant. Which I think mostly it is. The slowness comes
from what goes on behind the szenes. Mostly the different way of handling
stacks between Go and C creates quite some effort when calling into C and
returning. Those languages, which use "C compatible" stack and register
layouts get much faster C calling. That doesn't mean, they can call C as
easily.

So calling C from Go for small functions isn't a performance gain. You should
write those in Go. The calling of C is great for linking against larger
libraries, which cannot be ported to Go. And for that it works nicely and is
fast enough.

~~~
matthewmacleod
Yes, this would be the point I would make too.

I find I use Go quite often to build relatively compact tools and services
that need to use some features of a fully-featured and popular C library to
perform some complex function that would be expensive and time-consuming to
implement.

A recent example of this is using `libusb` and `libgphoto2` to enumerate DSLR
cameras attached over USB and capture images from them in response to some
other events, with a small web UI on top. It's maybe a few dozen lines of Go
to call some enumeration and capture functions, and then I get a nice byte
slice with all the data I want. There is minimal friction, the interaction is
clear, and any performance cost is worth paying because of the simplicity of
the interaction.

It's entirely true, and a well-known caveat, that the C FFI is slow. This
makes it inappropriate for some use-cases, but entirely suitable for others.

------
erokar
While there is certainly something to be said for simplicity in a language, I
think Go is too extreme in this regard. What it gains in simplicity it looses
in expressiveness. This leads to more lines of code per feature, and bugs in
software has been shown to be a function of LoCs [1,2,3].

One example of Go's lack of expressiveness is that loops are not abstracted
away, e.g. you can't use higher level constructs like map and filter on
collections. There are other languages that IMO strikes a better balance
between simplicity and expressiveness, like Python and Elixir.

1\.
[https://ieeexplore.ieee.org/document/1702967](https://ieeexplore.ieee.org/document/1702967)

2\.
[https://link.springer.com/chapter/10.1007/978-1-4471-3003-1_...](https://link.springer.com/chapter/10.1007/978-1-4471-3003-1_3)

3\.
[https://www.amazon.com/gp/product/0735619670/](https://www.amazon.com/gp/product/0735619670/)
(Chapter "Developer Testing")

~~~
Un1corn
I'm not sure I would have called Python simple, it's a great language and it's
not as complicated as something like C++ but it's not simple.

For example when should I use NamedTuple and when should I use a dataclass?
Should I be using a list comprehension or a map/filter/reduce? When should I
split that list comprehension? I have seen (and made myself) terribly long
lines with only list comprehension and it was hideous.

~~~
Scarbutt
You can write perfectly fine Python without any of the features you mentioned.

~~~
makapuf
Sure, but the more language that you may not use to write perfectly fine
programs but have to know to know the whole language the more it seems
superfluous. (See the whole drama over walrus operator). Python has
accumulated a bit of rust also, old ways of doing things that were replaced
with more modern ways, but that you should not use often. New languages or
languages that don't evolve fast don't have them. python is becoming more and
more complex (not tragically like c++) and v1.7 was indeed much simpler but
it's still a nice language considering its age and scars.

------
pcr910303
Ok, I understand that people that use Go has a different idea about it, but...

> Code duplication is more acceptable in the Go community than elsewhere.

> Many times, you’ll need to write that functionality yourself, or copy/paste
> it from a StackOverflow answer.

Am I the only person that this is regression? How can code-duplication be
defended by 'simplicity'?

> Fewer dependencies means fewer things that break while you let a project sit
> idle for a couple months.

I thought that pinning dependencies is _\the\_ solution for this? I don't use
Go, so I'm not sure about the Go packaging story, but is there no package
pinning solution? Yarn & npm handles this beautifully...

~~~
robfig
> Code duplication

It can be easier to maintain a small amount of code that you duplicate and
check in and is purpose-built to your use case and treat as your own, compared
to a general library imported for one routine that you have to think about
(licensing, upgrading, auditing). Or sometimes libraries break backwards
compatibility to add features you aren't interested in, and it makes busy work
to stay up to date.

> Is there no package pinning solution?

Multiple dependency managers have supported pinning since at least 5 years ago
e.g. glock, dep, glide. Go Modules supports pinning.

> Pinning is the solution for avoiding breakage due to dependencies

Related to the first point, in general you have to do work to upgrade your
dependencies. If not because you care about bug / security fixes that happen,
then because you use ANOTHER library that has the same dependency, and that
other library uses a feature only present in a newer version. Any time you
upgrade a library it takes work to verify nothing has broken, and potentially
work to fix things that do break.

~~~
littlestymaar
> It can be easier to maintain a small amount of code that you duplicate and
> check in and is purpose-built to your use case and treat as your own,
> compared to a general library imported for one routine that you have to
> think about (licensing, upgrading, auditing). Or sometimes libraries break
> backwards compatibility to add features you aren't interested in, and it
> makes busy work to stay up to date.

The overhead you're talking about isn't a real problem in my experience, as
long as your dependency manager has semver support (all of those I know).

When writing (or copying from SO) a simple snippet that's supposed to do just
what I want, almost 80% of the time there will be a bug in it (typically edge
cases are not covered)…

For instance, before Go 1.10, there was no rounding function in Go's std lib,
and 99% of the code you could find on the internet to do this (simple) task
was buggy: some failed with negative numbers, and others due to bad handling
of floating point numbers…

~~~
LandR
A lot of the answers on stack overflow are garbage. That whole site sometimes
feels like the blind leading the blind.

------
oefrha
> Just Enough Batteries Included

This is just an anecdote, but recently I decided to give golang a spin, and
wrote a small command line utility in it.

I won’t tell you the pain of trying to find an argument parser with support
for GNU-style long options. I suppose that’s something I should write myself
rather than use the standard library or find a third party library, because
simplicity. /s Edit: folks posted helpful suggestions, I take this paragraph
back.

Instead, let’s talk about something basic, regexp. Turns out golang’s regexp
package supports named capture groups, but if you want to actually refer to
the capture groups by names, you need to manually pair FindStringSubmatch
result with SubexpNames in a loop. Can’t make this shit up. Seriously, what’s
the point of named capture groups if you can’t refer to the groups by names?
If this is just enough battery included, your expectations might be a tad too
low.

~~~
nickcw
For gnu style long options you want:
[https://github.com/spf13/pflag](https://github.com/spf13/pflag)

It is a drop in replacement for the built in flag library.

I ususally start out with the built in flag library and migrate to pflag when
things get a bit more complicated.

~~~
oefrha
Thanks, not sure why I missed this. Probably should have used this instead of
settling on urfave/cli. I take back some of what I said.

------
sosodev
My feelings on Go are pretty mixed. I think a significant portion of software
can be described inside the box that you’re given with Go but if you enter
even moderately complicated territory it becomes a shit show.

I’ve had the displeasure of working on a pretty complicated Go project at work
and it’s given me plenty of counters to what OP is saying.

First, Go’s dependency management has been terrible and inconsistent for
years. Dependencies started very loose and without any versioning at all and I
was bitten many times by this mostly because developers used the looseness of
Go get to consume repositories that were never meant to be public APIs and
without versioning those repositories had zero guarantees of compatibility.

The community eventually noticed this was a problem and vendoring was
introduced but the community and Go itself flip flopped on a “correct”
solution for ages which ultimately led to even more package incompatibility.
Now we’ve got modules which is a step in the right direction but it’s going to
take quite some time for all of the Go projects to implement it correctly.
Modules have been nothing but a pain in the arse for us because our Go project
has a lot of dependencies.

Another thing I hate is the idea that people spread that Go’s simplicity leads
all developers to write simple, clean code. Some developers, I’ve noticed,
pine for abstraction like moths towards a lightbulb. In Go abstraction can be
very painful to do elegantly and I’ve seen some nightmares where developers
have tried. Using reflection on interfaces produces some of the shittiest code
I’ve seen in a long time and yet I’ve seen it repeatedly by different
developers in different organizations.

I must not be the only one who has noticed these issues because the creator of
Go himself made the Go Proverbs ([https://go-proverbs.github.io/](https://go-
proverbs.github.io/)) which include things like “interface is never clear” and
“reflection is never clear”.

Okay, I’m going to stop ranting now but I seriously don’t get the hard-on for
Go. I think I kinda got it before I used it professionally.

~~~
bob1029
This is exactly the take I have been expecting to unfold for a lot of
developers regarding Go. It is a very compelling language to get started with
because it is so reductionist compared to alternatives like Java and C#. This
makes learning it very straightforward, to the extent that you would almost
want to die defending the language that is feeding your dopamine loop.

But, one day you might wake up and realize it was all a fantasy. That is, if
you ever step foot into a shop where you are expected to build a line-of-
business application with sufficiently-complex business entities and rules.
All of your ideas about how wonderful Go is will fly directly out the window.
Sure, you can eventually implement anything in Go. It is Turing complete after
all. But, that isn't to say you'll have enough time to implement it. Go
advocates will happily dismiss the value of things like LINQ, generics &
reflection until they are faced with a reality where tools like these are the
most productive way get a feature into production before 2020 hits. If you
open your mind up to the possibility of using different (god forbid, unique)
approaches to solve problems, you might then find that Go does not even offer
the best implementation of these alternative language features.

~~~
alfalfasprout
Also have worked in some pretty massive Go codebases. It's about writing the
language idiomatically. If you're super concerned with trying to mimic Java-
style abstraction and shoe-horning OOP into Go you're going to have an awful
time.

If anything, I've found shipping code to be WAY faster in Go than Java due to
the much cleaner build system and approach to testing.

The one main area where Go struggles is in math-heavy code where the lack of
generics or algebraic types means duplication or code-gen.

But you're right. It's not the be all end all of languages. If anything, it's
just a much better substitute for the use cases where Java traditionally
excels.

------
andy_ppp
If it so simple why are the developer ergonomics so bad, if err != nil is a
mess (Elixir has a with clause that would be useful), everything points to
github master and module management is still extremely basic. The error
messages are absolutely atrocious compared with something like elm or even
python or Typescript, the type safety is a lie fairly often and you can still
write programs which crash due to that. Then there are go channels which are
kind of cool until you realise they can very easily lead to race conditions
and a version of the actor model would have been much better. I could go on
but go is like a nicer C rather than a modern language people should be
building web services in.

~~~
chrisseaton
Isn’t the actor model just as racy as channels? Race conditions everywhere is
one of the big downsides of actors compared to other concurrency models.

~~~
andy_ppp
Within any distributed system you can have races but in Golang the mutable
nature of channels makes this almost impossible to reason about (in my
experience most people use mutex’s in go unless doing something extremely
simple).

------
capableweb
People who likes Go's simplicity, have you ever looked into the even simpler
syntax of lisps? For example, Clojure have minimal amount of syntax, that once
you know, you know the entire language. The rest is just libraries that you
use, the actual program/data structures or language modifications (macros)
that each codebase usually employs a bit different depending on needs. But
lisp languages are really, really simple, even more simple than Golang (or any
C like language) can be.

Take a look at this (short) page which describes ALL the syntax of Clojure
(except macros) if you're interested:
[https://clojure.org/guides/learn/syntax](https://clojure.org/guides/learn/syntax)

~~~
robert_tweed
An important difference is that Go not only has a concise set of constructs,
it also tends to deliberately avoid abstraction.

So any given piece of code is very explicit and self-contained. Apart from
function calls, you can tell exactly what it does without looking up anything
else, like a macro definition for example. It similarly doesn't have a lot of
different ways to do the same thing tends to be opinionated about idioms
(gofmt, for example).

Something like a Lisp DSL would be antithetical to the Go model of what
simplicity means.

~~~
ken
> Apart from function calls, you can tell exactly what it does without looking
> up anything else, like a macro definition for example.

It sounds like you’re splitting hairs here. In my experience, macros aren’t
any more mysterious than functions. The only difference, after all, is when
they’re evaluated.

~~~
silasdavis
The difference is that they generate code. So now you have compile some code
in your head as well as running your hypothetical execution. Nothing wrong
with that but they tend to be harder to understand than functions.

------
tetha
After some work with go, I wouldn't call it simplicity that much. Simplicity
has too many implications and expectations and by now, I'd call simplicity
context dependent. In the right context, spring boot can simplify backend
code, in the right context, libraries like panda can simplify data crunching
algorithms in python. And even as this thread shows, "simplicity" immediately
blows up into discussions between the different kinds of simplicity.

I rather tend to call what go does regularity and the language design of go
does promote said regularity. Regularity in this context means: Go codebases
and lots of go code tends to follow very similar patterns.

And this in turn makes it simple to digest even complex code bases, at least
to me. A go codebase might just contain more code, but chasing some listener
on the result of another listener through multiple annotations and config
files in a java codebase... yeh.

And on top of that, this kind of regularity, as well as the kinda small set of
libraries to do something makes it easier to get started with the language in
my experience, especially for people who aren't seasoned developers.

------
petilon
I wish developers of TypeScript would learn from Go. TypeScript is awesome,
but it is getting larger and larger and larger with every release. Soon it
will become like the US tax code, and no one person will know the entire
language.

~~~
frou_dh
The armchair psychologist in me considers there to be two camps of programmer
in this regard. One that gets distressed by something like not having the
entire language in their head, and the other that doesn't ponder that kind of
quandary in the first place.

~~~
phamilton
I've shifted toward the latter as I've embraced type systems that can
substitute for having the entire language in my head.

At the end of the day, I rarely care about the language. I usually just care
about my data morphisms. And a good type system helps me immensely in that
regard.

------
enraged_camel
My first car was a 2002 Honda Civic, bottom of the line. Aside from automatic
transmission, everything - doors, locks, and so on - was manual. It didn't
have a CD player or even A/C. You might say it was a simple car.

At first I loved its simplicity. It did its basic function (getting me from
one place to another) quite well. I drove it only occasionally, as I was in
college, and it didn't fail me in any way.

Then I graduated and got a job, and started commuting. Once I found myself
driving that car every single day, I started to resent its simplicity. In a
few occasions it bit me in the ass - one time, my passenger forgot to lock
their door after getting out, and overnight someone got in and rummaged
through the car and stole a bunch of stuff. That would not have happened if it
had automatic locks!

That's how I feel about Go: if you're writing simple microservices
occasionally, then it's a good language. But as usage becomes more frequent
and serious, the simplicity starts to seriously hold one back. I understand
the reasons behind that simplicity, but, as with all things, too much of a
good thing can in fact be bad.

------
api
When I write in Go I spend almost all my energy thinking about the thing I am
writing, not the language. It's a breath of fresh air, like modern C with a
safety lock on the footgun.

I learned how to code "thin" low abstraction C++ and un-clever dynamic code in
languages like Ruby and JS as I matured as a programmer, but it takes
discipline. When I was younger I fell like so many others into the trap of
showing off my command of languages instead of showing off my command of
algorithms and ability to solve problems elegantly.

I don't find Go that ugly, especially with standard 'gofmt' formatting. It's
so simple the brain learns to parse it as effortlessly as the compiler.

The brilliance of a program is its results / its complexity, with the greatest
code ever written being simultaneously the simplest and most impactful.

Go makes that maturity natural and easy. I think it's a great influence on the
programming zeitgeist as well as a nice language.

Haven't tried Rust yet but my impression is that its somewhere between C++ and
Go. Can anyone comment on Rust's cognitive load?

~~~
steveklabnik
Rust has an interesting relationship with cognitive load. At first, it is
fairly high. But then, at some point, stuff just clicks and it drops
dramatically. How long that takes depends on the person, they’re background,
how much support they have, etc. As a random anecdote, we had a team at work
who started a Rust project, and it took them a month or two to get going. Now
they’re sailing quite smoothly. (We have other teams as well but I’m only
really familiar with the process for one of them.)

The reason for this is that you have to learn to work _with_ the compiler and
its rules. At first, that’s a struggle. But later, it’s just helpful, and you
have to think about things way less because the compiler is helping you keep
track of them. It’s like a pair programmer.

~~~
tfha
Have you used a lot of go? I'm a big fan of rust and definitely like rust devs
and the rust community, but I find that people haven't used go enough to "get"
it don't really understand just how much it can take away from cognitive load.

Rust and go are not really comparable in this regard, even if rust is much
better than most people realize. There are too many ways to approach a problem
in rust.

That said, definitely there are plenty of places where rust is a much better
language choice than go

~~~
steveklabnik
I’m not making any point about Go in my post, not trying to compare them, only
to speak about Rust’s situation here, since that’s the question that was
asked.

I have written some, but not a ton, of go. It is opinionated in ways that are
very different than my own opinions, and so I didn’t enjoy it very much. That
said, I deeply respect the Go team, and am glad others really like it. I’m not
really the best person to compare the two languages, which is why I try not to
do it.

------
millstone
I don't know Go, but from my outsider's perspective it doesn't look simple!

For example compare inserting into a list in Python:

    
    
        list.insert(index, obj)
    

with apparently idiomatic Go:

    
    
        a = append(a[:i], append([]T{x}, a[i:]...)...)
    

Or compare Python's `list.reverse()` to Go's recommendation:

    
    
        for i := len(a)/2-1; i >= 0; i-- {
            opp := len(a)-1-i
            a[i], a[opp] = a[opp], a[i]
        }
    

This seems quite error prone.

Idiomatic Go taken from
[https://github.com/golang/go/wiki/SliceTricks](https://github.com/golang/go/wiki/SliceTricks)

~~~
andrenth
One of the things to keep in mind with Go is that in general it doesn’t try to
hide possibly costly operations under simple to use interface.

So in your example, append may need to allocate, and the fact that it returns
a new pointer makes that clear.

For the second example, you could use sort.Reverse, but the loop makes the
cost of the operation clear.

~~~
CAMLORN
In the insert case call the function InsertWithAllocqation, and also provide
an Insert variant that doesn't allocate but can fail. In the reversing case,
call the function ReverseInPlace, then require the user to make a copy either
before or after. This gets you the same goal--not hiding the costly allocation
--while also making it convenient to use and removing the possibility of
mistakes.

The real problem is that without generics, such functions actually have to be
written in the same fashion as sorting where you provide a function for
reading and writing both, as opposed to just working--so the "simple" loops
which are indeed error prone actually become less code, and we all use them
instead. I highly, highly doubt even the best Google programmer will always
get the boundary condition on that reverse a list loop right especially in an
emergency or at 3 AM, and this is a very simple case where Go just can't
abstract on par with anything-with-generics.

But my point is that you can meet the goal of not hiding operations just with
good naming, if that's what you consider a terminal value of good pl design,
and don't need to forego abstraction to do it.

------
bfrog
Go is so simple that there are dozens of code generators and project
dependency management solutions. Yet people live to espouse it's simplicity.
It's simple like C in that regard, you have to roll your own solutions to
avoid copy pasta hell if you want type safety and sane project management. The
real mistake though is the lack of an Option type. The inability to express
optional differently from passing references efficiently is a large test case
burden.

~~~
hu3
Based on what you wrote I can't help but think you haven't used Go extensively
or at all.

Code generators are rarely used and Go has an official built-in dependency
management tool: go modules.

[https://blog.golang.org/using-go-modules](https://blog.golang.org/using-go-
modules)

~~~
bfrog
I wrote 100kloc rest service using go, so actually I do know a thing or two.

Granted this was 4 or 5 years ago so perhaps things have improved.

------
martincmartin
In woodworking, the only tool you need is a hammer and chisel. With enough
effort, you can make a decent table leg, you don't need a lathe.

But if you do it several times, you think "if I just invest time into learning
the lathe, I can save a lot more time when making table legs. In fact, I might
be able to make a more complicated table, since the time and effort saved when
making the legs can now be spent on other things."

------
lacker
I’ve been working in a Node codebase for a bit, and I am really missing Go’s
built-in profiler and benchmarking support. It’s pretty well integrated into
the default tooling, and easy to use.

~~~
XCSme
What about using the Chrome Dev Tools for Node profiling?

~~~
lacker
The node tools & chrome dev tools just seem very poorly integrated by
comparison with the go tools. In go, if you have a unit test and you want to
profile the code in it, it's just a few code changes to set that up. To use
the chrome dev tools, you have to connect to some running process with a
browser, which requires at least rearchitecting whatever you're doing so that
it's an indefinitely running process. The built-in node profiling is sort of
medium in ease of use, but low quality in the information it returns. Often
it's hard to figure out what high-level function calls correlate with the low-
level performance info.

------
photon_lines
If anyone is interested in learning Go, I made a relatively simple web-server
with demo apps which you can find here: [https://github.com/photonlines/Go-
Web-Server](https://github.com/photonlines/Go-Web-Server)

I don't agree with other people calling the language simple. It makes doing
something like building a web-server from scratch very simple, but the actual
language syntax and expressiveness is lacking in my opinion.

------
vpmpaul
Follow up article in months..

Go's Simplicity is its weakness for XXX thing.

I find these constant take one aspect of a language and paint the world with
it to be unhelpful and distracting. However based on the comment section HN
eats it up, no wonder people keep writing them. Its like Instagram for nerds I
guess.

------
dana321
Some great points about go:

If you don't use it, remove it (compiler checks to see if a variable / module
import is actually used)

The real pleasure in go vs c/c++ is compiling / linking is easy with the new
go modules support, multi-platform out of the box. Kinda like cargo in rust.
Kinda.

Downside is.. C/C++ modules are 2nd class citizens that can slow your whole
code down.. Conversion is much better, but usually someone has done it yet..
sqlite needs a go port.

I think of go as an advanced assembly language with some c-like idioms and a
powerful runtime to handle processor thread scheduling.

~~~
pstuart
Unfortunately no recent activity, but this comes to mind:
[https://github.com/elliotchance/c2go](https://github.com/elliotchance/c2go)

------
erikpukinskis
I’m curious, has anyone here had an actual need for genetics in production
code? I mean, like you actually have an uncountable or at least large number
of types that you want to push through a generic mechanism?

I use “generics” in TypeScript but it’s always a very small number of types:
usually two, maybe up to 10...

There’s no reason I couldn’t do that without generics, just by writing a
couple line plugin for each type that converts it to a common representation.

I’m not convinced that’s even strictly worse than generics from a usability
perspective.

~~~
snagglegaggle
Yeah. The usual case is a container type or composing with a container type,
but there are many "algebraic" types that you may wish to use if they could
accept types to parametrize them over.

------
suresh70
I'm new to Golang and have been exploring it for a while. Go isn't simple to
me when it comes to web dev. It starts with the scarcity of resources to learn
from. I'm looking for how to structure the codebase(boilerplates, best
practices etc), how to go about unit and integration testing, dependency and
configuration management. Are there any resources somebody here can point to
regarding these topics?.

~~~
dhconnelly
[https://www.usegolang.com](https://www.usegolang.com) from Jon Calhoun, who
is well-known in the community, could be useful

------
cygned
The biggest pain point I have is the lack of nil-able vars. Maybe it doesn’t
make sense in term of type systems, maybe it is that way for reasons of
reliability - but in practice it’s a pain to work with JSON APIs or NULLable
fields in DMBS’ if you come from a language where these things just work.

------
Keats
> In my opinion, of recent languages, Go has the sanest versioning and
> dependency story. I haven’t had a single breakage due to an update in Go.

I don't know about that. I've tried to build 3 Go applications in the last
couple of years and none worked because some of their dependencies had
breaking changes in master. Hoping that someone doesn't push a breaking change
on master, doesn't change username or doesn't delete their repo doesn't seem
very sane to me. At least vendoring is well supported.

------
jeffrallen
Go is simple enough that your idiot co-workers can understand it. (Remember:
They think the same about you, and they are right.)

------
AzzieElbab
I do not find go simple. Too many hidden conventions that my brain refuses to
memorize because I do not see any logic behind them

------
apatheticonion
It's important to remember that Go isn't by itself enough to write quality
software.

Developers must arm themselves with established and tested software design
patterns to utilize the language effectively.

The same goes for other languages as well, however Go's emphasis on providing
a tiny toolset amplifies the benefits/consequences.

... Also we need generics

------
pmoriarty
Is Go simple?

It looks positively complex compared to Scheme.

~~~
13415
I have a direct comparison, since I've been programming substantial amounts of
code in both languages. I'd say that Go is much simpler in practice. Think
about all those classical functional LISP exercises to recursively manipulate
lists such as reverse a list, return the nth element, flatten lists, combine
lists in various ways, and so on. I doubt you can write good Scheme code
without having done those exercises at least once in your life. And at least
for complete beginners these exercises are not easy.

There is no equivalent to this in the Go world. Of course, you could do it,
you can do functional programming in any language. It's just not usual.
Paradigmatic Go code is simple, imperative style with a lot of mutation. You
only need to learn how structures and interfaces work and your good to go.

~~~
pmoriarty
_" Think about all those classical functional LISP exercises to recursively
manipulate lists such as reverse a list, return the nth element, flatten
lists, combine lists in various ways, and so on. I doubt you can write good
Scheme code without having done those exercises at least once in your life.
And at least for complete beginners these exercises are not easy."_

In practice, you'd just use a library function for pretty much all of those.

Nobody writes those from scratch in Lisp or Scheme except as an exercise.

Also, once you do get the hang of it, recursion is a much simpler and more
natural way of expressing many algorithms. But it too is not necessary in Lisp
or Scheme, and you're welcome to write the iterative equivalent, if that's
what floats your boat.

As for writing in an imperative vs functional style, I personally find the
latter simpler and more reliable. But nobody forces you to write in a
functional style in Lisp or Scheme. They're both multi-paradigm languages, and
you can write in an imperative style in either, if you prefer.

------
FpUser
Without digging too much into syntax, issue of GC there is a simple ani-
feature in go:

I can not create and control native threads. While Goroutines are nice and
needed feature some of my software simply can not function without the native
threads. Hence Go is no go for me.

------
ausjke
C is popular also partially due to its simplicity by design. Javascript
fatigue is caused by its 'complexity(imperfectness)' in design, but it took a
good spot by accident however bad it was designed.

------
floatboth
[https://twitter.com/vertexclique/status/1194902103929569280](https://twitter.com/vertexclique/status/1194902103929569280)

------
ausjke
Does golang have an option to use 'shared' libraries/modules? while it is
great its size is unfit for so many embedded devices, tinygo is a good try,
but still.

~~~
takeda
As far as I know it doesn't. One of the selling point is that everything is a
single executable.

------
Animats
I tend to agree. I was a big fan of Rust at first, but they put in too much
stuff. Borrowing is brilliant. The semi-functional programming features, not
so much.

Go was created so Google people could write server-side stuff in something
less brittle than C++. It's a good match for that. The library stability is a
good thing. Those are libraries Google uses internally, so you know they're
executing millions of transactions a second somewhere. The error cases have
been encountered already.

~~~
zozbot234
The "semi-functional" programming features have always been a part of Rust -
some of them have even been removed since, such as typestate. The borrow
checker as we know it was actually added quite a bit afterwards.

~~~
verdagon
Fascinating! I thought Rust was built around the borrow checker. Did Rust
ensure memory safety back then? Back then, was their goal to make a more
modern C++ with functional influence maybe?

------
ryan_glass
This article really, really makes me want to give Go a shot - more than
anything I've read about the language before.

~~~
takeda
You should, you either will love it or hate it. I personally don't like it,
but from your response it sounds like you might have a different opinion.

------
azth
golang is too simple. Real projects are complex, and having a simplistic
(dumb) language just pushes complexity to the programmer instead of having it
built in to the language and compiler.

There is a balance to reach, and languages like Java do a much better job at
this than golang.

------
YesThatTom2
Go’s simplicity means I have to focus most of my time on solving the problem
instead of showing off that I know (insert inscrutable feature) and figuring
out what the last person to touch the code when they used (other inscrutable
feature).

That sucks! Why sh I kid I help the company solve their business problem when
I could be spending all afternoon debugging (insert language issue)

------
agumonkey
There's something about russ cox writings that exemplifies go 'cleanliness',
they engineer stuff to make a task clearly, nicely and nothing more.

People complain about the lack of generics but I wonder if they didn't allow
it to avoid an abstraction explosion, at the cost of redudant/copypasting.

------
ryanmccullagh
I _hate_ Go’s encouragement and use of single letter variable names.

------
b0rsuk
In Go's case, it's too early to tell how it scales. It has the opposite
approach to readability as Perl.

I wonder if Go ends up sharing Perl's fate. Two languages that make people
feel very productive, but don't seem to scale very well.

~~~
rollcat
Define "scale"... Large codebases? Teams? Long-lived projects? So far Go looks
excellent at all of these

------
edgarvm
Go is not a simple language, it lacks of features.

------
pjmlp
Expecting mass exodus from Go developer to Oberon-07 as the language starts
adopting more features.

The "simplicity" is shown in the amount of code generator frameworks.

~~~
musicale
I like Oberon, both the language and the system, but I don't see either
expanding beyond niche usage, in spite of their elegance and compactness.

~~~
pjmlp
I was being sarcastic, as Wirth is probably the only language designer that
removes features with each language revision.

While I loved my experience as Oberon user/dev, and it opened my mind to
systems programming using GC languages, my favourite languages on Wirth's
linage aren't done by him, rather influenced by his creations.

Modula-3 (which comes from Mesa/Cedar, not Oberon), Active Oberon (another
ETHZ team), Zonnon, Delphi.

------
kristianpaul
I would love to use Go just if it wasn’t for the garbage collector

~~~
bsaul
Go is compiled to assembly. What makes you think it's an interpreted language
?

~~~
kristianpaul
Garbage collection

~~~
saagarjha
They’re not related.

