
Warming up to Go - scapbi
http://jeremymikkola.com/posts/2015_08_28_warming_up_to_go.html
======
pcwalton
> Whenever I work in Rust, I find myself having a good time mucking around
> with the abstractions, but not really getting anything done toward the
> problem I’m trying to solve.

A couple of points:

1\. Realistically, this is a programmer problem that no language can solve. If
you don't need the abstractions for the code you're writing, don't use them!
This is hardly something unique to Rust. A common complaint that experienced
Go developers have about new Go programmers is that they overuse channels and
goroutines--mechanisms of abstraction (of control flow, broadly construed).
But nobody is arguing that Go would be a better language without channels and
goroutines. The solution is to train new Go programmers to stop overusing
channels and goroutines and to focus on the problem at hand. That's the same
advice that I would give to anybody thinking about the perfect generic way to
solve their problem in Rust instead of getting the job done.

2\. Generics aren't exactly optional in Rust. Let's assume for the sake of
argument that generics are a bad feature that languages should not have
(something I completely disagree with, but in any case). Even supposing this
were true, it's not like Rust would be a better language without them. Rather,
the language _could not exist_ , because it would be impossible to satisfy its
core goal of zero-overhead, runtimeless memory safety. You couldn't have smart
pointers without generics; you couldn't have iterators; you couldn't have
Option; you couldn't have any functions that take and return pointers at all
because lifetimes are a form of generics. Go is able to get away with not
having generics because it leans heavily on having a runtime to support the
built-in data structures and on garbage collection to make memory management
decisions at runtime. Not so with Rust, which is designed to bake all that
information into the compiler to avoid the overhead of a runtime and of
garbage collection.

~~~
chipsy
When it comes down to it, everyone seems to have their pet abstraction.
Everyone except the old grumpy programmers. And YAGNI always applies, to some
degree, because you can always build back up to some approximation of the
"gotta have" abstraction from cruder primitives.

So the benefit of the language design is to make it so that the abstractions
you _do_ use play well with everyone's code instead of being a ball of duct-
tape and easily broken conventions.

I think that is missed when people go looking for a language with power tools.
You can already do things too powerful to comprehend in C if you add enough
macros and pointer indirection. But if you sit down trying to solve a problem
using fewer language features instead of more, you usually come out ahead -
because then you're forced back towards first principles about what the
project needs and see the abstractions in the proper light. Powerful languages
lead to an ecosystem of correspondingly powerful libraries, which drags you
into the mire no matter what.

While both Rust and Go are at a lifecycle stage where they're dogged by
language window-shoppers, Go is actively resisting the people who didn't
intend to make a purchase, leading to "Go not powerful" whinge blogs. We end
up knowing less about Rust's productivity because most of the early adopters
are going to be the kinds of people who seek innovative ways to break
compilers - demanding customers, who don't give much back.

That doesn't mean that Rust is actually unproductive, as it's quite likely
that if you try to write C-style code with few ornaments, you will have little
difficulty doing so, and the technology will mostly break in your favor. But
it does mean that it'll take longer to work up momentum for a production-ready
ecosystem, since the conversation will continue to revolve around "ooh, we've
never done that before's" with borrow checking and metaprogramming. And when
it's all over, it's likely that simpler ways to do these things will come to
light - either in Rust or in another language.

~~~
pcwalton
> And YAGNI always applies, to some degree, because you can always build back
> up to some approximation of the "gotta have" abstraction from cruder
> primitives.

What I was saying with point (2) is that this is explicitly not true for Rust.

~~~
chipsy
Yes and no - it's a tradeoff abstraction. The borrow checker is a whole-
language design directive, and its strength comes from giving up something in
other areas - in this case, having to use generics everywhere. A language, as
I said, is mostly made up of these intentional tradeoffs to try to find an
appropriate balance.

You can still _express_ the abstractions of Rust through another language,
though it may take writing another compiler on top to actually do so - they
just won't hold the same utility, because the utility of this abstraction is
mainly tied to the "zero-cost" implementation method, not to whether it has
expressive powers.

------
coldtea
> _For someone new to the codebase who knows nothing of the giant abstraction,
> which do you think is easier to grasp: 10 lines of code that only make sense
> if you know the abstraction, or 20 or 100 lines of code that can be
> understood on their own?_

I'll take those 10 lines of code.

We've only managed to build powerful systems because of ever increasing
abstractions.

See Alan Kay's DSL research for example:
[http://www.vpri.org/pdf/tr2007008_steps.pdf](http://www.vpri.org/pdf/tr2007008_steps.pdf)

It's not like every system with abstractions has to be done the Java way.
Functional programming, be it Haskell or Lisp etc, has tons of abstrations
too, and they don't get in the way.

I'd say Java's issue wasn't abstractions per se, but uneeded abstractions, BS
boilerplate and needless ceremony. Which Golang also suffers from.

Besides, you don't just dispose of the abstractions and get the same, but now
straightforward, code. You get rid of them and get 10x the code (the 100 lines
compared to 10 he mentions), which you have to keep track of and keep in your
head, only now you don't have high level abstractions to help you in that, you
have to do it manually.

(And of course Go vs Python is not a good comparison, as Python's lack of
types make it difficult to reason about large programs quickly. How about Go
vs C#/Swift/D etc? I'd take Swift any day of the week.

~~~
danneu
There are always trade-offs.

For example, I'd rather read explicit 100 lines of explicit SQL than the 10
liner Active Record abstractions I've had to come back to on my old Rails
projects.

It turns out that the 10 liner forces you to keep much more in your head (the
whole Active Record abstraction, its idiosyncrasies, its edge cases, its
performance profile) than the 100 lines of explicit SQL that let you bootstrap
a mental model without having to credentialize in documentation every time you
touch the project.

I'd also rather read 100 lines of explicit authentication middleware than 10
lines of Devise configuration (a Rails authentication gem) for the same
reason.

The appeal of "keeping less in my head", as we put it, is one of the main
reasons I use small frameworks these days.

------
bluejekyll
But the expense of extra lines of coke come at significant maintenance costs.
It means needing more test and the inevitable bugs that will be discovered.

More lines of code is inherently a bad thing, while I'm all for simplifying
the code so that people can understand it, I'm not a fan of doing that at the
cost of increasing the amount of maintenance that will be needed on the code
base overtime.

~~~
PuerkitoBio
> But the expense of extra lines of co[d]e come at significant maintenance
> costs.

All lines are not created equal.

    
    
        if err != nil {
            return err
        }
    

Those 3 lines have very, very little maintenance cost.

The obvious must also be mentioned: less lines of code usually mean building
on abstractions - the lines of code are in there, somewhere, but not in your
code. Better hope that abstraction is well-tested. Of course it's all a matter
of balance, but I disagree with "more lines of code is inherently a bad
thing", which stated another way, would mean "cram as much information as
possible in a single line of code".

In the end, lines of code are a bad metric of software.

~~~
jacques_chester
> _Those 3 lines have very, very little maintenance cost._

These three lines are everywhere, give me no clear indication of whether they
are intentional behaviour or merely a boilerplate-by-convention, and worst of
all, they obscure the actual business logic.

~~~
PuerkitoBio
Error handling does not obscure the business logic, it _is_ very much part of
the business logic.

~~~
moe
Error handling _does_ obscure the business logic when, as in Go, nearly every
operation needs to be followed by a boilerplate guard clause.

Other languages (e.g. Java) have solved this problem quite elegantly over a
decade ago with the introduction of Checked Exceptions[1].

Since this is the pattern that every Go program ends up emulating anyway,
Google could save everyone a lot of work by just baking it into the language.

In the eternal words of Larry Wall:

    
    
      The computer should be doing the hard work.
      That's what it's paid to do, after all.
    

[1]
[https://en.wikipedia.org/wiki/Exception_handling#Checked_exc...](https://en.wikipedia.org/wiki/Exception_handling#Checked_exceptions)

~~~
fooster
Sorry, but if you have every operation followed by a guard clause you are
doing it wrong.

For example,

[https://blog.golang.org/errors-are-values](https://blog.golang.org/errors-
are-values)

~~~
moe
Your link describes (and encourages) exactly the laborious emulation of
Checked Exceptions that I was talking about in my above comment.

It doesn't make the guard clauses disappear. It merely forces the programmer
to manually aggregate them at a higher level with even more boilerplate code.

~~~
mahyarm
If my guard clause is doing the same thing for 20 lines, it increases
readability. Could be an optimization point too!

------
MichaelGG
I wonder if Go is taking off due to folks coming from Python and Ruby, looking
for something that has better perf and concurrency. Sorta like how Python got
a lot of Java users that were just sick of the boilerplate.

The fact he compares Rust and Go is bizarre - they're totally different types
of environments. Why not toss in OCaml and Java, too? Go is a managed runtime
language and "systems" is only mentioned due to misleading marketing when the
language launched. And of course Rust might feel a bit harder - no runtime
means you gotta do all that stuff at compile time. Kills the chance for some
shortcuts!

People go on about how Go is practical and you don't need features, really.
But that's a lame excuse. Facebook was built on PHP. Transport Tycoon was
written in x86 assembly. Harry Potter was written with pen and paper. So you
don't _need_.

The attitude from the Go community is almost like an anti-intellectual one
isn't it? F#'s inventor said it was hard getting generics into .NET because
Microsoft viewed it as "academic and experimental" despite it being old ideas
put into practice. Yet another decade later and Go is further promoting this
anti-progress attitude.

(I'm not really against Go, just disappointed in how weak available language
technology is.)

~~~
lobster_johnson
> folks coming from Python and Ruby, looking for something that has better
> perf and concurrency

Long-time Ruby developer here, relunctant Go developer. Absolutely.

I want performance, concurrency, efficient memory usage, and static typing.
Ruby is lacking in all these areas. Go is mildly better, but is regressive in
many other respects.

For me, Go is a step back in terms of productivity, but a step forward in
creating stable, fault-tolerant code. It's a compromise. Go feels like a
stopgap solution until something better comes along.

For example, I've learned to design the internals of my code around a
functional style that makes code composable and the data flow simple. Go
doesn't support that style, not really. Go promotes simplicity, but a lot of
stuff that should be simple in Go isn't "simple". Like mapping and reducing an
array.

What's interesting is that working in Go reminds me a _lot_ of using Borland's
ObjectPascal back in the mid-1990s: A small, natively compiled (and super fast
to compile), not-functional language with built-in reflection and a stupidly
strict, cumbersome type system. Go makes very few innovations over
ObjectPascal, and inherits many of its problems; Go's notorious lack of
generics, for example, caused _exactly_ the same issues back then. And nobody
should be surprised about that.

Currently, the language that looks closest to my ideal is Nimrod.
Unfortuantely, it has almost zero mind share, and has a surprising amount of
odd, off-putting warts for such a young language (perhaps not too surprising
when you realize that for many years it was just one guy hacking away at it,
TempleOS-style), disappointing since many design choices just feel just right,
like someone has read my mind. Currently I'm on the fence with Nimrod.
Jonathan Blow's jai also looks promising.

~~~
kibwen
Since you mentioned having experience with Ruby, have you looked at Crystal as
a statically-typed alternative with a deliberate Ruby influence? From what
I've seen of it so far it may fit your purpose precisely.

~~~
lobster_johnson
Crystal does indeed look interesting and promising. But it will be a few years
until I'm willing to consider it for production code, I think.

------
duggan
> That said, for some projects I’d prefer Rust. Specifically for very large
> projects, a heavier reliance on abstractions starts to make more sense.

This is an interesting point, because I've never actually worked on a large
project that shipped. Lots of brow furrowing and hand wringing over
correctness, but at the end of the day nothing for customers to use, or too
late for it to matter - which is more or less the same thing.

Conversely, I've seen plenty of small "shim" projects that grew, sometimes
rapidly, into much larger projects. Few, or no tests. Awkward, meandering
blocks of logic, often with developers lamenting the lack of good abstractions
(myself included).

Large projects tend to be second systems, and second systems tend to suffer
second system effects.

It seems as though the profession of software development curses its
practitioners to remain unsatisfiable.

~~~
nickbauman
I've worked on many (if not all) parts of systems that are over 1,000,000 SLOC
written in languages like Java and C++. I always felt that if I'd used a more
powerful _language_ those systems would be expressible at a 1/5 to 1/10 the
SLOC.

Once I discovered Lisp, I found that more powerful language. In my experience
(I'm now a full time Golang developer) for the things that Go does well,
Clojure creates _much_ smaller amounts of code and performs at least as well.
When I program in Go, I revisit my younger self again feeling the same
frustrations. I don't see Go as The Way Forward except for small
"infrastructure" programs in the M2M space.

~~~
Denzel
I've never been a Go evangelist. For years, the appeal and praise lavished
upon Go flummoxed me. Until I decided to use Go for my latest product. Then it
made sense. For most purposes Go is "good enough." And it's good enough in
enough areas for me to set Go as the standard, de facto language at my
company, unless there's a damn good reason to use another language.

I appreciate Go because it's fast, small (re: syntax and standard library),
garbage collected with nice primitives (pointers and slices), concurrent with
high-level primitives (goroutines, channels, select), expressive enough
(strings, maps, range, first-class functions, type system, etc.), first-class
support for Unicode, and it has a rock-solid standard library.

About a month ago, I started learning Go. It's so small. The language, the
standard library, the tools. They're all so easily digestible; it took me 4
days to work through the Go playground, read the entire Go specification, work
through and learn 40% of the standard library, and start writing production-
ready programs. C was the last language that enjoyed a language/standard
library this small, to me.

The same thing can't be said for Ruby or PHP, or any other languages I've
worked with. In almost every project I've been involved with, there would be
usage of some arcane corner of the language. Whether it's determining the byte
offset of a member function in C++, or the GCC extensions to structure
initialization in C, or metaprogramming magic in Ruby. All these cracks and
crevices are difficult to keep in your head. This isn't so with Go. It's easy
to keep the entire language and standard library in your head; providing a
certain ease and flow when building a program.

Ease of use applies to memory management as well. Go offers just enough
control over memory to make it pleasant to implement and use real data
structures. Try implementing an LRU cache in Ruby/Python that evicts based
upon an object-size policy without wasting a ton of memory simply maintaining
a linked list. When I built a distributed real-time, type-ahead service in
Ruby I was fighting the language the whole way when it came to simple data
structures, memory management, and concurrency. (It was just a prototype, I
know Ruby wasn't the right tool.) Originally, I was going to rebuild the
service in C++, but that would've taken too much time. Instead, I opted for Go
and it was much easier, and obviously much more performant. Not as performant
as an equivalent C++ service, but performant enough.

I don't think much needs to be said about Go's concurrency. First-class
primitives such as goroutines, channels, and selects along with solid standard
library support in sync and sync/atomic, makes for a powerful combination. Go
wraps these concepts in a very bland, mainstream way. Making them more
accessible to more people with little time investment.

You're right, Go will not win based upon the # of LOC written. However, after
writing 20k+ LOC in Go over the past month, I don't see any reason to write
any of our back-end code (infrastructure, services, web servers, tcp servers,
etc.) in anything other than Go. It's good enough in enough areas to make it
much faster for a team to standardize on just this one language. Not only does
this reduce the time wasted due to context switching between languages in a
polyglot shop, it makes it a ton easier for anyone to jump in and contribute
anywhere at anytime.

\---

To give my thoughts some context, here's a subset of my professional and
personal language/project history:

\- Ruby: 100k+ LOC Rails monstrosities and smaller 20k LOC projects

\- PHP: 200k+ LOC bare-PHP e-commerce websites and 50k LOC CodeIgniter/Laravel
intranet apps

\- Python: 10-20k LOC utility and server management scripts

\- C++: 500k+ LOC open-source 3D game engine and multiple <10k LOC personal
projects

\- JavaScript: ~35k+ LOC for the front-end work on some websites I've worked
on

\- Common Lisp: read all of Practical Common Lisp and ANSI Common Lisp, and
dabbled in a few small programs

~~~
mahyarm
For me a big thing is avoiding error prone inconsistencies and other footguns.
The less footguns a language has, the better.

For example in objective-c, most of the language deals with nil items without
crashing or errors, except for a few APIs and collections. This abstraction
mismatch in collections causes runtime crashes and is a language footgun.

Swift fixes this footgun with optional types, but objective-c could of made
it's collections nil-safe and apple could of just declared in the company that
your apis have to be able to handle nil arguments, no exceptions. The new
nullable annotations are an ugly patch to just help transition to swift and
still do not fix the footgun.

Java's NPE is another example of a footgun, and C++ & C have footguns
everywhere that generate billions of dollars of security industry work.

What would you call the footguns of lisp & golang?

~~~
Denzel
Off the top of my head, for Go, (1) the semantics around nil and closed
channels resulting in deadlocks, and (2) failing to recover from an unexpected
panic. I think error handling and documentation of the errors returned by
methods could be improved in Go. I dislike having to comb through the Go
standard library source to see what types of errors a method returns.

I can't speak for lisp because I don't have enough experience with it.

~~~
masklinn
Go also has the exact same NPE footgun Java has, IIRC.

------
struct
I've found that there are only two reasons I like Go: 1) It's concurrency
model was a revelation compared to anything I did in the C world. 2) It's
opinionated.

Everything else is a bonus. In Go, there's only one way to format your program
correctly, there's only one way to document your program correctly, there's
only one recommended way to serialize to JSON, it only has one (IMO really
awesome package management system) etc. It's remarkable how little thought I
now need to put into those things.

~~~
iofj
I disagree strongly. You should keep into account that Go is a very young
language, and hasn't had to deal with historical baggage. It's still the hot
new thing, and that just won't last.

1) Concurrency model : there are plenty of C++ frameworks doing various
concurrency models. As long as you do what Go does, and only use that
framework, no linking to other stuff, it works beautifully. Already, the
standard library is making allowances for other synchronization primitives
(look at the sync package)

Of course, in C++, you'll be sorely tempted to link in code with other
concurrency primitives. Especially when it comes to having 2 kinds of event
loops, you really shouldn't do that. It doesn't help that a lot of large
companies felt the need for 2-3 proprietary C++ event loop systems.
Fortunately, most libraries aren't parallelized, so it doesn't really matter
what event loop system you run them under.

Also, from working with other concurrency models ... I miss a lot of stuff.
Futures ... oh my God, does Go need that (why doesn't "go func()" return a
future ? AARGH). Limiting resources for specific functions like java's
executor framework ... when you need it, there's no going without it.'

Also, select is a horrible way to implement protocols (rules-based
communication between either different parts of the same process or simply
other parts of a distributed system). Implementing a protocol as a set of
methods that can be called on a given object, with or without remote object
references works much better. Incidentally, this is what all Go protocol
libraries do (google protobuf, cap'n proto, flatbuffers, ...). I understand
why they choose this approach over the "native" Go approach : select isn't
useful for anything but the most basic of protocols. If you have more than 2-3
message types ... things start to get completely out of hand with select.

2) only one way to format your program correctly ?

I think you'll find that gofmt in fact leaves many questions unanswered. Line
break or no ? Up to you. Where ? Up to you. Full syntax for struct
initializers or short syntax ? Up to you, gofmt won't change it ...

What I will credit gofmt with is popularizing the concept, and getting code
formatters much more opinionated in other languages, and that is a very good
thing. But LLVM's C++ formatter and autopep8 are superior in functionality to
gofmt for their respective languages.

3) I have seen at least 5 different JSON serialization libraries in Go.
There's 3 broad approaches.

You can either use reflection on structs and then use that to decode JSON.
Trouble is, this is slow ... very very slow. It's as slow as the next option,
but doing things this way does mostly ensure correctness (doesn't deal with
various kinds of ddos though). There's just "decoding" JSON into
map[string]interface{}, which is about as fast but it can deal with fields
unknown at compile time, which can be important if you're making something
that, say, just checks one aspect of requests (e.g. security, or load
balancing). The huge disadvantage is casting interface{} to what you need
every time. And finally there's precompiling a given JSON format into a Go
library, which is a LOT faster than either of the previous approaches (but
also can't deal with unknown fields).

4) Package management ... lacks versioning. There's no guarantee your code
will compile 6 months later if you use Go's own package management. In fact,
given just how in flux everything still is, it's pretty much guaranteed to not
even compile.

Also, a number of Go advantages are only advantages because Go is only
beginning to exit it's honeymoon phase. Go libraries, whether builtin or on
github, are "v1" designs : they're consistent, they don't have historical crap
in them, they don't have 20 unforeseen but necessary usecases crammed in in
extremely uncomfortable ways. Not yet, that is : the honeymoon is ending. Bad
design decisions do bite larger programs on occasion (such as Go's logging not
using an interface, which can bite you, leaving you with little choice but to
reimplement logging for your project/company). This means that while things
are pretty good now when it comes to the standard library and most github
libraries, they're getting worse fast.

Go libraries are written by enthousiasts mostly at the moment. So while
ignoring errors occurs regularly in those libraries, it's not yet like C,
where every single error is ignored in the vast majority of libraries.

~~~
ansible
_But LLVM 's C++ formatter and autopep8 are superior in functionality to gofmt
for their respective languages._

I was under the impression that some of the syntax for C++ and / or C is a bit
ambiguous, which makes it difficult to parse correctly, and to make source
code transformations that will absolutely, positively not change the semantics
of the code.

At any rate, getting more or less the entire community onboard with using one
particular code formatter is the really hard part. I'd say it is impossible to
do with existing languages. I don't see how the majority of in-production C++
code, or Python code will ever be formatted in one fixed style.

So even if superior formatters exist in those languages, it doesn't matter,
because the majority of code I will encounter out in the wild (open source or
not) will not be formatted in with a single convention.

If there is one lasting legacy from the creation of golang, it is hope that
for all new languages, their communities will adopt a single code formatting
convention. I hope that everyone, whether or not they like golang itself, sees
how important that is moving forward.

~~~
coldtea
> _I was under the impression that some of the syntax for C++ and / or C is a
> bit ambiguous, which makes it difficult to parse correctly, and to make
> source code transformations that will absolutely, positively not change the
> semantics of the code._

That's for naive parsing of C++, like some do for syntax highlighting etc --
not for LLVM's C++ formatter, which uses the AST of the fully blown C++
compiler et al.

If that parser didn't get everything right, then LLVM's C++ output will also
be borked, not just the formatter.

------
actsasbuffoon
I'd contend that abstraction is perfectly fine, so long as it's an abstraction
that people reading the code are likely to be familiar with. In Haskell, I'd
prefer someone just use a monoid when appropriate, rather than reinventing the
wheel. That said, you might want to avoid some of those Haskell niceties when
writing Scala (as my co-workers keep reminding me) because those abstractions
are less familiar in the Scala community.

As always, the key is to tailor your message to your audience. That's just as
true when writing code and choosing your abstractions.

------
nv-vn
>For someone new to the codebase who knows nothing of the giant abstraction,
which do you think is easier to grasp: 10 lines of code that only make sense
if you know the abstraction, or 20 or 100 lines of code that can be understood
on their own?

The "only make sense if you know the abstraction" part seems to have it
backwards. The point of the abstraction is to _not_ have to know what's being
abstracted away. The problem is that with Java (which Go was being compared to
here), abstractions often times aren't exactly abstracting away anything.
"Abstraction" has basically come to mean "use objects because they have
getters and setters!" and other things like that, which give very little
benefit in general. Nine times out of ten, they do make the code more complex,
because their abstractions come either at too low a level (abstract away
simple functions but require the code on the outside to implement everything
and reinvent the wheel) or too high a level (abstract away all operations so
that no creativity is possible with this). So as a solution to the
"abstraction" problem, Go does it right, but something like Rust (which
doesn't try (too hard) to be like C, Java, etc.) can do an even better job at
this by entirely changing the meaning of "abstraction".

------
slantedview
"In real-world code, you don’t actually end up casting interface{} to
something else that often"

I have to disagree here, and I've had trouble finding any serious argument
that this is an OK thing to have to do. When we accept the benefit of not
allowing users to create generic abstractions, we also have to recognize the
very real cost of not allowing users to create generic abstractions.

Software/technology trends go in cycles. Several years from now, a generation
of Golang programmers will be preaching the benefits of DRY, having learned
from the painful mistakes of maintaining the large and complex Golang
codebases yet to come without the benefit of creating appropriate
abstractions, just as Golang was partially an answer to the painful mistakes
of maintaining large and complex dynamic codebases. And so the pendulum will
swing again.

------
shadowmint

        Whenever I work in Rust, I find myself having a good time mucking around 
        with the abstractions, but not really getting anything done toward the 
        problem I’m trying to solve.
    

I hear this a lot.

There's a lot of good stuff in rust, but people seem to get massively caught
up in making the borrow checker happy and lose sight of the actual task.

Maybe the idea of a drop in GC type for rust (ie.
[https://github.com/Manishearth/rust-gc/](https://github.com/Manishearth/rust-
gc/)) has some merit after all...

~~~
pcwalton
The original post was talking about _abstractions_. The borrow check is not an
abstraction.

~~~
shadowmint
Ok, sure:

    
    
        Making the borrow checker happy with their abstractions...
    

You've seen iton #rust too, Im sure. People trying to make a genric reusable
thread safe Foo and getting stuck with the borrow checker rather than making a
specifc solution to a problem that uses unsafe or whatever and actually solves
the problem.

I know _Ive_ seen it several times.

Safe good code is better, but it's also a distraction in many problem domains.

------
bmurphy1976
I just want to add my 2 cents.

Abstraction is fine, and useful, however every abstraction comes with a cost.
Even if you can't see or quantify the cost that does not mean it isn't there.
It only means you have not done the work to fully understand it. It is your
job as a rockstar dev to ensure the benefits of using abstraction outweigh the
costs.

And that brings us to Go. Go is a perfectly capable language. Like most it's
good at some things and not so good at others. If you approach it expecting
Haskell, you will fail. If you approach expecting Go, you will have a much
better experience (assuming you are working on a project that is a good fit,
once again, your responsibility).

I won't tell you to use or not use Go or Haskell. They are both awesome, and
they both utterly suck. But you know what? That's life. That's how it is
always going to be.

Pick your poison.

------
Mikeb85
The timing is funny, I just started playing with Go a few days ago (the 1.5
release as well as the impressive amount of software that's actually been
shipped with Go convinced me to give it a go - pun intended, somewhat).

I must say, its a great little language. Love the tooling, the dependency
management, it was even incredibly simple to set up the dev environment
(basically just DLed the binaries, added to my path, installed the Go-plus
package for Atom, and everything just worked).

It is a little shocking to work with a relatively bare-bones language since I
work mostly with R (which has a package for everything it seems), but its been
a lot of fun. I'll probably do something bigger with it in the next little
while.

------
aikah
And fortunately , there are plenty of alternative languages (D, Rust, Nim,
Crystal,...) , so let's promote them instead because any criticism Go will
fall into deaf ears anyway.

~~~
sdogruyol
Yeah,especially Crystal is gaining traction and lots of good commits are
coming in.

~~~
ebiester
Is Crystal's whole point to be a staticly typed Ruby, or is there more to it?

(I guess I'd just like some of the things we've learned from functional type
systems, such as pattern matching, which I don't see up front.)

~~~
sdogruyol
Well that's the starting point. Currently more ideas are forming up and being
baked into the language.

For example Crystal is removing explicit Threads and switching to Channels as
the main concurrency model.

------
excepttheweasel
> In many cases, that up-front cost is paid back many fold later, when other
> programmers (or you yourself, after you’ve forgotten the details of the
> code) come and try to read and work with the code. Go optimizes more for
> understanding the code later than for building big things quickly.

I believe this sounds attractive in part because programmers are so excited by
the idea of solving problems, and it's true that a specialized implementation
can always beat a general one. But it does not sound particularly sustainable.

A language that doesn't provide adequate abstractions often results in
requiring its users to develop those missing abstractions over and over again.
With the requisite possibility of bugs or implementation naivety. As both a
Rust and Go user, I do find the talk of generics a little over played. Go has
great facilities for code reuse that often mean you won't miss generics as
much as you might think. But I also see Go projects growing in size quicker
than I can appreciate. The increased volume of code, each piece being
specialized for its particular purpose - harder to fit in my head at one time.
The cost of changing the code becomes more severe, each instance of an
abstraction in the code being different enough to warrant specialized
attention, rather than fixing code in a single place.

In every organization I've worked at, as the size of a code base grows, so
does the cost of change. A language that doesn't scale well is only going to
exacerbate that problem. Perhaps Go seems great now because we've yet to
really see any projects written in it at this scale.

------
rumcajz
There'a real problem with abstraction. Look at any Java code. If you haven't
seen it before you are going to deperately browse around the code to find out
a single line that actually does something. But all you see is scaffolding.
When you finally find a line that does actual work, it's not clear how it
connect to other such lines. There's another such line 1000 lines below it but
all the abstraction makes it almost impossible to find out how are the two
executed. Which one goes first? Is one of them optional? Et c.

In general, abstractions are used to capture programmer's understanding of the
structure of the program (it's more or less like boxes and arrow sketches in
your notebook). But then, programmer's understanding of the structure of the
program is often inadequate and lacking. As your understanding of the problem
improves, so does your abstraction. But by then, it's hard-coded and cannot be
easily changed. Many times I've seen how new functionality cannot be
implemented because it doesn't fit the abstraction and re-writing the
abstraction would mean re-writing the whole project.

~~~
tome
> But all you see is scaffolding. When you finally find a line that does
> actual work, it's not clear how it connect to other such lines. There's
> another such line 1000 lines below it but all the abstraction makes it
> almost impossible to find out how are the two executed.

Wow, but as you say, that's "scaffolding" (or "boilerplate"), not
"abstraction".

~~~
rumcajz
As I understand it, the scaffolding in Java exists to express astraction, no?

------
jeffreyrogers
From what I've seen most of the times people complain about Go it is because
they think they're going to use whatever feature they claim is lacking when in
actuality they rarely use it in practice.

~~~
aidenn0
I like rarely used features. A language having extra features doesn't make it
worse. I've used maybe 5% of the python standard library, but that fact
doesn't make me feel like Python is worse.

~~~
meowface
There's a big difference between "features" and "libraries".

~~~
aidenn0
Care to explain? As an example of a rarely used feature I'm glad to have:
Common Lisp macros.

------
bsummer4
The article spends a lot of time talking about how "Complicated" abstractions
are, and about how abstractions come with a complexity cost. IMHO, the
opposite is true.

> Whenever I work in Rust, I find myself having a good time mucking around
> with the abstractions.

 _Good_ abstractions, the kind you use in Haskell and (presumably) Rust are
simple, non-leaky, and exist to _enable simple, correct code_.

It's true that abstractions, even good abstractions, take time to learn.
However, once you've digested them, you'll see them everywhere, and you can
continue to use them for the rest of your life. Functors, for example, are a
foundational abstraction. They are extremely simple, extremely powerful, and
will relevant forever.

------
jodah
It's been interesting to watch similar arguments being made of Golang, over
and over. From a casual user's perspective, it seems to optimize for (what is
to me) the wrong thing, which is readability over maintainability. Without the
ability to create abstractions we run the risk of having to re-learn the
lessons of the past, about DRY and SRP and all that jazz. While more language
features give users more rope to hang themselves with, less language features
constrict the ability of larger projects to manage complexity, such as is the
case with dynamic languages. For Go, I sense that this will become more
obvious over time.

TLDR; Readable code is nice, but maintainable code is better.

------
cschep
great ending!

"Typing is not what makes programming difficult."

------
sprash
Nim ([http://nim-lang.org](http://nim-lang.org)) is far superior to Go and
Rust in many ways.

~~~
MichaelGG
Nim is interesting but isn't remotely like Rust. It doesn't have memory safety
as a core goal. And there's no strong sense of design and elegance. It very
much comes off as "here's a bunch of ideas thrown together with the
restriction that they all must somehow compile to C".

I'm not saying it's bad or its users are wrong, but the mindset is utterly
different.

Rust and Go don't belong in any comparsion really. The only reason they are
compared is because Google co-opted the term "systems" to mean something
different than everyone else. Go fits in much more compared to managed
languages, like Java or OCaml or something. (Just because Go statically links
its runtime into output binaries doesn't change things. You can do this with
C# via Mono's AOT features, for instance.)

~~~
burntsushi
> The only reason they are compared is because Google co-opted the term
> "systems" to mean something different than everyone else.

Really? You know, for a fact, that that is the _only_ reason the two languages
are compared? There couldn't be any other possible reason?

How many years has it been since the official literature made such an
egregious and offensive mistake as to misuse the term "systems"? The _only
reason_ it's even an issue is because people insist on bringing it up as the
root cause for [insert social phenomenon here]. Because clearly, anyone who
compares these languages must be so enamored by the original description of Go
as a "systems" language that there couldn't _possibly_ be any other
explanation. (But maybe this fits in just right with your view of the Go
community as "anti-intellectual.")

Maybe people compare the languages because many people find them comparable.
There's no mystery there.

~~~
matthewmacleod
_There 's no mystery there._

Contrary to the previous poster, I suspect people simply compare the languages
because they appeared around the same time and are both backed by big players.

There's nothing really comparable between them (although the that's just my
opinion) and I'm baffled as to why they are co pared so often.

~~~
burntsushi
Any two languages can be compared. The notion that the _act_ of comparing two
languages can be traced to one particular root cause (such as the use of the
word "systems" N years ago) is ludicrous to me. What matters is the analysis
that is involved in that comparison.

For example, I'd find the answer to this question very interesting: "Compare
and contrast concurrency in Rust and Go." I contend that the answer to this
question is _epic_. It will take you back through the history of PL and lots
of really interesting design decisions. At the forefront: should concurrency
be a first class part of the language? Or should it be part of a library?

And that's just one interesting question. There are plenty more.

------
djhworld
I like Go, I've coded a few personal projects in it in the past, and generally
like to keep up with the Go scene when I can.

However, I can't help but feel that if people want it to replace Java, then
they need to make a robust IDE like IntelliJ or Eclipse.

I'm a Vim user at heart, but when coding in Java I find IntelliJ + IdeaVim
plugin to be very productive. It goes far beyond just the classic auto-
complete too, IntelliJ does a lot of neat stuff like suggesting updates to the
code, or creating fields/methods and refactoring just as a simple key
combination.

~~~
douche
If Go has any legs, I'm sure that JetBrains will get around to building a
version of IntelliJ for it. They've even got empty space for it in the IDE
section of the products menu on their site :-)

~~~
tylerpachal
For the meantime, there is a very nice Go plugin for IntelliJ products:

[https://github.com/go-lang-plugin-org/go-lang-idea-
plugin](https://github.com/go-lang-plugin-org/go-lang-idea-plugin)

------
meowface
I think if/when Go adds some form of generics (beyond requiring code
generation with comments...), it will truly replace Java.

