Hacker News new | past | comments | ask | show | jobs | submit login
Half a decade with Go (golang.org)
259 points by geetarista on Nov 10, 2014 | hide | past | favorite | 250 comments



OK, I'll admit it. I've spent six months with Go. I keep waiting for the moment when I understand it, maybe even develop some enthusiasm for it, and reach Pike-enlightenment.

And I pretty much hate the language. I feel like I'm writing in something that combines the worst weaknesses of Pascal and Java. In fact, Mark Dominus' comments about Java (http://blog.plover.com/prog/Java.html ) approximate my experience Go far better than I would have guessed when I started the project I'm on.

People who seem to be smart nevertheless keep talking it up... some not even as just a good tool but as their favorite language.

So I'll ask: What is it I need to read/work through in order to at least "get" Go and really understand its strengths (whether or not I end up liking it)?


I don't think you're going to come around on it. It's not suddenly going to stop being terrible at abstraction. That essay seems very accurate and applies to Golang.

The strengths of Go to me seem to be its extensive standard libary and the fact that you don't need to worry about writing elegant code because it's not really an option. That reduced decision making is actually really appealing to me on some level, but it's not enough for me to consider it over what I consider better tools (Haskell, Rust).

I'm currently porting a tiny program a friend wrote in Go to Haskell so we can compare notes, and I think the Haskell is both safer and more elegant, but I'm jealous of the fact that all the libraries he used are from the standard library while I have to evaluate libs of varying quality. However, I would rather put my time in to improving the Haskell ecosystem than using something convenient that makes a lot of decisions I disagree with.

edit: I also think there's value in being a conventional imperative language as someone mentioned below. I with there were something between Go and Haskell. Perhaps that'd essentially be Rust with immutable data structures and garbage collection.


I dont know much about go, but I always thought that the problem with 'the standard library does everything' is that it starts to feel heavy and better alternatives pop up. Then you have these dark places in the library that most people avoid, and you cant really throw away.

Clojure used to have something called, clojure contrib and it was kind of awsome, it did pretty much everything in just one jar. It was also a huge problem to mentain and everybody wanted to be in contrib, so you had the one json lib in contrib and fasters outside.

With clojure 1.3 it got split up into many diffrent parts and it still servs as somewhat of a community standard or recommended library. Overall its much better and much more usful, but sometimes I miss the days when everything was in one jar on on dependency.


Rust is avoiding the contrib problem by shipping a robust package manager from the beginning and pushing much of the Rust stdlib (and language features) into libraries.

http://crates.io/


I started using Clojure just after 1.3 came out so I could be wrong, but my impression was that while contrib was all in one place, it was still maintained by a bunch of random people who got their code into contrib, rather than being treated as a core part of the language the way (I believe) Golang's is.


Yes it was, like I said it is better now. However it was also kind of nice because everything was just there. Even with good mantainers, some of the same problems could keep existing.


That's right. contrib was always the red-headed stepchild to core.


Composition. That is the single biggest thing that has impressed me as my codebase has grown. Concurrency and messaging is nice, but I come from Erlang... I am not easily impressed by concurrency and messaging. Composition, the power of interfaces in complex systems is the key for me. It is what makes me stay with Go, and why I will probably stick around for a long time. It is so obnoxiously useful, without ever getting in my way. Let's talk for a second about the tiny little function

io.Copy(dst io.Writer, src io.Reader)

It reads data from reader and writes to writer... simple. Now what makes this little function so darn useful is it takes anything fulfilling its interfaces (io.Writer and io.Reader). The first way you will probably use it will be to copy between some stream and a file without having to eat up all the memory to store the buffer (not using ioutil.ReadAll for example)... but then you realize you can use a gzip compressor on the writer side, or a network socket, or your own code... and io.Copy works with anything that fulfills its interface.

As you build out a complex application, you start by creating your own functions that take advantage of existing interfaces foo.OCR(dst io.Writer, src img.Image). After that you start building out your own interfaces... like a MultiImage interface that has ImageCount and ReadImage methods that returns the count of images and binary data... but then you realize the images could be big, so you make the ReadImage method return an io.Reader... and now you have gone full circle and are using io.Copy to copy imageX from a stack of images to return to your OCR function that will output the data to an io.Writer which you made actually a gzip writer because text compresses well.

Beyond composition -- obviously, the concurrency and messaging is nice and when you need it vital. The other thing that will help make Go "click" is being very "data oriented" in your design... be vicious and minimal: http://youtu.be/rX0ItVEVjHc (great talk on data oriented design) and be absolutely pragmatic, focus on getting shit done, always...


>It reads data from reader and writes to writer... simple. Now what makes this little function so darn useful is it takes anything fulfilling its interfaces (io.Writer and io.Reader). The first way you will probably use it will be to copy between some stream and a file without having to eat up all the memory to store the buffer (not using ioutil.ReadAll for example)... but then you realize you can use a gzip compressor on the writer side, or a network socket, or your own code... and io.Copy works with anything that fulfills its interface

And how is that any different than any language with interfaces? (Besides the implicit thing?).


Because of the implicit thing! The api designer doesn't have to write the interface, you can do it yourself. As long as naming conventions are kept to and the signature matches, you can apply this anywhere.

Imagine a close() interface in Java. There isn't one - but having a try {...} finally { x.close(); } can be very useful sometimes. But having interface graphs made of granular interfaces makes everything slow and causes a lot of complexity when you try to think about your type hierarchy. That's why interfaces always just grow and you can't use them any longer because other classes only implement part of the api. Something usable for all of awt, swing, file handling, random foreign libraries? Unthinkable. Also, adding something in a later release (like CharSequence in 1.4) can have a wide ranging impact and requires you to change a lot of code.

In Go, you just add an interface from the union set of multiple structs api - and you can use it. No matter who wrote those structs. The value is enormous. Think of it as something like dependency injection at compile time.


>Imagine a close() interface in Java. There isn't one

https://docs.oracle.com/javase/7/docs/api/java/lang/AutoClos...

You can even leave out the finally when you use an autocloseable resource.

    //r will be closed no matter what
    try(Resource r = getResource()) {

    } catch(SomeException e) {

    }


My Java is getting rusty... I read about this sometime somewhere but forgot it, thanks! I should have picked something like "String getText()", then.


> Because of the implicit thing!

Known as structural typing and available in most modern languages.


available in most modern languages

Well, "modern" is an ill-defined concept. As far as I'm aware, structural typing is not really that common, is it? Besides OCaml and Scala, is there any relevant (used outside of academia) language that supports it?


Would rust's "traits" count as structural typing? (I know Rust may not count as "relevant (used outside of academia)" yet, but maybe in the future.)


D and C++ templates for example.

F# also supports it, given its ML linage.

C# tricks with dynamic, although in this case it is dynamic typing, so not really the same thing.


The disadvantage of C++ templates is the structural type is implicit - you only know if the input object satisfies the type if you read the documentation, code, or can decipher the error message that occurs if it didn't.

Concepts would have fixed this, but we don't have concepts and maybe never will!


> Concepts would have fixed this, but we don't have concepts and maybe never will!

enable_if and type traits are a workaround for the time being.

Concepts lite will definitely be in the next revision.


Haskell's typeclass provide it.


Typeclasses aren't structural typing, they are nominative typing, as typing is controlled by explicit declaration of relations between types and typeclasses, not inferred from structural properties.


Sure they are: (forall a. X a => a) is a supertype of (forall a. (X a, Y a) => a).


I stand corrected.


Like duck typing?


I suspect this is like duck typing with some verification before compilation : the compiler checks that the object sent as parameter implements the correct interface.


It's called structural typing.


So, compile-time duck typing.


So, wikipedia says (http://en.wikipedia.org/wiki/Duck_typing#Structural_type_sys...):

"Duck typing is similar to but distinct from structural typing. Structural typing is a static typing system that determines type compatibility and equivalence by a type's structure, whereas duck typing is dynamic and determines type compatibility by only that part of a type's structure that is accessed during run time."

So it sounds like "duck typing" is defined in terms of run-time semantics. I guess you might be able to have genuine "compile-time duck typing" in a dependently typed language, which could be a good reason to preserve the distinction.

Otherwise, yes, they are certainly similar.


In the same Formula 1 is performance go-karting.


No, structural typing. It has a name already.


> Now what makes this little function so darn useful is it takes anything fulfilling its interfaces (io.Writer and io.Reader).

I've implemented InputStreams from byzantine transport layers in Java that work with the standard library. I don't quite understand what is special about this concept in Go (maybe it's nice for people coming from typeless, messy dynamic languages or nice languages with horribly inflexible and ad-hoc standard libraries like Python and Ruby).


Go has nothing unique (and maybe that makes it special). I mean that sincerely, everything it has, has been done dozens of times. Interfaces in Go are implicit (which is important, IMHO) and really, tremendously simple. These two features make them exceptional easy to use, and ACTUALLY used.

I have used interfaces (or equivalent concepts) in dozens of languages and they always felt like far more of a chore, explicitly using X interface or Y interface, ugly complex declarative specs, etc. Go just makes it painless.


replace Go with Python in the text, and you won't see a difference: there are iterable, keyable, file-like interfaces, etc. And creating your own is trivial.


Yes, but Python doesn't compile to a single binary, and usually runs much slower.

What people like about Go is its mix of features, not some specific one by itself.


Depends on the implementation.


Python doesn't verify the interface is met until run time.


With all the cases were Go pushes you to use interface{} that's the case in Go a lot of the time too.

If it had Generics that would be another thing.


With all the cases were Go pushes you to use interface{} that's the case in Go a lot of the time too.

However, you can wrap an interface{} using container (for example), with type-specific input and output functions, getting back compile-time type-checking. Yes, it is not optimal, and yes, you're still paying the price for type assertions.

If it had Generics that would be another thing.

I would like to see a good solution for this that integrates well with the rest of the language.


I would like to see a ho-hum solution for this that's on par with what's offered on other languages.

I say that because the Go designers, despite having designed a quite mediocre language, suddenly when it comes to generics the want the perfect no-compromises solution or nothing.


You can make an io.Reader interface in any language. Interfaces don't really shine until you start composing them.

Say you have a type that implements the io.Reader, io.Writer, io.Closer and io.Seeker interfaces, eg. an os.File. This type would also automatically implement io.ReadCloser, io.WriteCloser, io.ReadWriter and io.ReadWriteCloser, io.ReadSeeker, io.ReadWriteSeeker, io.ReadWriteSeekerCloser, io.WriteSeeker, io.SeekCloser etc.


Yeah, I simply think that this is hard to explain in a comment how useful and easy it is. But, I gave it my best Go...


Composition

I read your comment, but this seems more like A sane standard library with proper interfaces and not a language feature per se (also composition to me sounds like the pattern where you create objects which are composed of other objects but that's not what you mean, right)? What you display here can be done in pretty much any language suporting inheritance (or I'm missing something), so your point is those languages don't have io.Copy out of the box?


I think the point here is implicit inheritance. There are many interfaces in Go, but you don't need to specify which interface you are implementing. So you can easily implement io.Reader and io.Writer among many others, without extending your type declaration for lines on end.

This means that many standard types in Go implements either io.Reader and/or io.Writer. That's neat. The implicit interface implementation is definitely a language feature, and the libraries are making good use of it.

(That is not to say, you could not do the same in other languages, but you would have to specify the interface they implement rather than just adding a method with a specific layout.[0])

[0] I am not remembering the right term right now.


I suspect you will come to hate this feature. Just because an class happens to have a method called close() does not mean it works the same way as another class that also has a close method. An interface is much deeper than the prototypes of its methods, and pretending you can pattern match an API to whatever names a code author happened to pick is likely to lead to pain eventually ...


The very point of an interface is to decouple the caller from all the implementation specifically because it will work differently between implementation. If you expect two classes to implement it the same way, you an abstract base, not an interface.

Close is a particularly good example. One need only look at C#'s IDisposable to see that it does, in fact, work well. A mock might noop it, another class might close an FD, and yet another might make an RPC call.

I agree that interfaces tend to have fairly narrow family tree. And, by this narrowness, there's little ambiguity about what T GetById(id int) means. As the tree expands, which happens with implicit interfaces, ambiguity is more likely. Nevertheless, there's a fairly large common vocabulary that we'd all largely agree on. Closer, Reader, Writer, Logger, etc. Even in more complex ones, I see little risk of confusion, say, http.ResponseWriter. And, something that I've noticed from Go (which I never did in C# or Java), is the tendency to favor very small interfaces, which ends up being pretty awesome.

That aside, consider that implicit interfaces allow the consumer to define the interface. For example, you create a library that has a concrete struct called MyStruct with a method called DoStuff(). You define no interface because you don't need one.

I, a consumer of your library, need an interface because in some cases I'm using your MyStruct to DoStuff and in other cases, I'm using my own implementation. So I create an interface, define DoStuff(), and BAM!, your structure now implements my interface. I don't have to change your code.

Sure, the workaround is to wrap your structure in my own which implements the interface. But how, in this case, is the implicit interface not a huge win?

Maybe, as you say, it'll screw over people who use it poorly. For everyone else, I see no drawbacks.


It's not just "people who use it poorly". The point of an interface is to abstract over some details while guaranteeing others. If I am unaware of an interface, I don't know to avoid the names used in that interface, and I don't know to abide by the invariants assumed in that interface. That seems like it will bite people who've done nothing wrong. If I am providing a library, I can't possibly be aware of every interface anyone might define in code that uses it. I've no clue how frequently this will occur, in practice.

Haskell's typeclasses work around this by letting you define "how a type implements an interface" at either the definition of the interface or the definition of the type. (Or, strictly, anywhere else both are in scope - but that gets messy for a few reasons, so it's discouraged and GHC warns about "orphan instances" unless you tell it not to.)


If I am unaware of an interface, I don't know to avoid the names used in that interface, and I don't know to abide by the invariants assumed in that interface. That seems like it will bite people who've done nothing wrong. If I am providing a library, I can't possibly be aware of every interface anyone might define in code that uses it. I've no clue how frequently this will occur, in practice.

That's not a problem with Go's module system. If someone is using your library, and wants to use your interface in a particular package, that's fine. If they want to use another library, with another interface of the same name in another package, that's also fine.

If they want to use both libraries in the same client package, they'll have to locally rename one or both of the imports.


It's entirely possible that I just don't know enough about go's interfaces. Wasn't it explicitly stated up-thread that you don't need to name the interfaces you support? If that's the case, I don't see how you get around the possibility of a type seeming to support (because of what's defined for it) an interface that it doesn't (because those functions actually do other things - obviously or subtly).


Here's a (very contrived) example of what I think you mean.

Here's a container interface. It has a sort() method, which sorts the elements of the container.

Here's a different container interface. It's got a method that tells you what kind of concrete container you have, so you can understand how expensive actions like insert and sort are going to be. But the author was from England, and so called that method "sort".

You can see how that's going to (fail to) work out when you define a "sortable" interface...


Right, thank you!

Traditional (but even more contrived) examples include "draw" and "fire" doing unexpected things when applied to a gun as opposed to an image or an employee.


ok, ok. I think I'm understanding. Let me propose a middle ground.

First, you really have to split this up into two distinct cases.

The most powerful/useful is when you create an interface in your package for a structure in a different package. It's been years since I've done .NET or Java, but I remember frequently cursing a library developer (often someone at Microsoft or Sun) for exposing a concrete type instead of an interface. In this case, I don't see any downside. Creating a:

type Render interface { Draw() }

And then using my (or someone else's) Gun structure, is no different than using the Gun structure directly and expecting it to render itself to the screen rather than come out of its holster. The interface buys you the level of indirection, but whether you use the interface or the concrete type, it's up to you to know WTF Draw() does.

This first approach is well explained at [1]

In the second case, where a library expects an interface, I agree there's a risk:

// 3rd party package package fastRender

type Drawable interface { Draw() }

func Render(d Drawable) { ... }

// my code: fastRender.Render(new(Gun))

I've lost some degree or type checking. Since my Gun's Draw doesn't explicitly implement fastRender.Drawable, maybe I'm trying to fit a square peg into a round hole. There's nothing the compiler can do to help either.

It sounds a bit like an academic concern to me. Is this something that's actually bitten people in the ass? How is it different than any method that takes primitives? I can pass a []byte of a bitmap to a Music player's Play(data []byte) method...

[1] https://medium.com/@rakyll/interface-pollution-in-go-7d58bcc...


Note that I'm not arguing for Java. Haskell typeclasses covers both of these use cases (you can make a new type an instance of an existing typeclass, or existing types instances of a new typeclass, without touching any exsisting code) but requires someone to look at it and say "This actually implements the contract that interface assumes." They can be wrong about that, of course, but I think making it explicit helps.

There's also a subtle third case - I import two libraries. One of them defines Drawable, the other has something that looks like it implements Drawable. But it's just happened upon the same signature, and makes different assumptions. Maybe it even says it implements Drawable - but a different Drawable defined in a third library.

As I've said all along, I don't know how often these things are hit in practice. I think so far go development has been comparatively centralized, and so it might be seen less often than it would if it were used everywhere, but that might still be rare enough to not be a problem.


thanks


OK, now I understand. It may be possible to muck things up if you've got two different types (from different packages) that have the exact same signatures for all the functions defined in the interfaces.

It doesn't seem like a high probability that this would happen on accident, though it might be possible to contrive an example.

I'll have to think about that.


Certainly the larger an interface is, the less likely that someone supports all of it by accident. Of course, that's both good and bad - if the accidental support would have been correct then we're missing out, but I think most of the time we shouldn't count on that.

One could possibly force the issue by including an unused function with a deliberately unique name (supports_interface_foo, or maybe a UUID or both) as part of the interface. Can functions added later fill parts of interfaces? If so, this approach would make the situation roughly that of Haskell typeclasses. If not, then this approach makes the situation roughly that of Java interfaces. In either case, it might be appropriate to some pickier interfaces.


Thinking about it in the abstract, it seems to me that the probability goes up as interfaces get smaller/narrower (which apparently is a style that go encourages) and the codebase gets larger (which is go's target environment).


It goes down as method names contain more entropy, too.


True. And yet... for a good method name in an interface, you want it to be as general as the interface could be. You want sort(), not sortVector(), because sorting is much more general than sorting a vector - you could sort a tree, if it can be ordered.

You could include the name of the interface as part of the method name, though. But the downside of that approach is, if another interface wants to re-use the same method, things get awkward...


Structural types, as opposed to nominal types?


Basically. But Go obviously also have nominal types.


No, io.Copy was just the example that everyone would recognize in Go, and likely the first interface you will touch.

My point was implicit, exceptional simple interfaces are sorta magic. They are effortless, and because they are implicit, there is no harm in creating a 1 function interface (or 10 of them). Because your caller is never going to have to do an ... Implements ThingA, ThingB, ThingC, ThingD, ThingE, Thi... it implicitly supports an interface if it fulfills the signature.

So your interfaces end up being tiny (just what you need) and pervasive. This means the way your system ends up working tends to be far more aligned along interfaces than anything else.

When I speak of composition (in this case the composition of functions) I am speaking of the ability to use functions together rather freely and with little effort. This is a bit hard to explain, but it feels a bit like a modern shell, you can wire together lots of commands (functions), from lots of places that have no awareness of each other with the pipe | operator. In Go, simple, minimalistic interfaces act as the glue and let you compose lots of diverse functions together exceptionally quickly.


I NEED to work Go into one of my projects, but dammit I love Python so much. it is a warm and safe and comfortable cocoon. :)


Then you need to scale and your library isn't actually built in C... or only runs on 2.x (or 3.x) and the cocoon seals up and you can't escape... you scream but no one can hear you... you look for help, desperately clawing at cython, numpy, jython, pypy and C extensions -- they all require you to leave your cocoon far behind... you struggle and break free... suddenly you are exposed to the big wide world outside of your cocoon... you look back and realize the cocoon was just a cleverly disguised prison. /hackernewsstorytime


hahah well I am very comfortable with C and other languages, but I love python for it's versatility and ease of use! It has some very expressive one liners that are surprisingly coherent for being one liners.


Just keep in mind Go IS NOT like python, but instead it's a better C

I'd say it's a better C, not a better C++ (or Java/C#)


As a language maybe but for usage, not so much.

For me, most modern uses of C fall into 3 camps:

+ extreme portability

+ complete, low level resource control

+ foundational libraries

And Go doesn't really qualify for any of these. For the latter 2 largely because of the GC.

I think in practice, that its engineering bent i.e. no frills language and high quality tooling, will more likely see it being used in the server-side application/middleware space. I guess not surprisingly.

So it really is up against Java, Python, Ruby and C++ and is, therefore, interesting in that it isn't trying to compete on lingustic goodies.


"As a language maybe but for usage, not so much"

Yeah, I agree with your points. Which is a shame really.


I loved python and wouldn't've given any of it up. Then I switched to Scala and discovered how much I could get the computer to do for me, without giving up the conciseness, readability and expressiveness of Python.


So I'll ask: What is it I need to read/work through in order to at least "get" Go and really understand its strengths (whether or not I end up liking it)?

As discussed elsewhere on this page, Go is not the most feature-rich language out there.

But it does have features that seem to help my solve my problems with a minimum amount of fuss. Like using interfaces liberally, instead of having to define classes. Having lexical closures is also nice.

If you're not working on an application that can benefit from concurrency, you may not see all the benefits of Go either.

What kind of problems were you trying to solve, and how did Go see heavyweight compared to how you'd solve the problem in some other language?


I like functions as first-class values and closures. If they weren't present, I might well have bailed months ago. :) And from various experiences doing OO with other languages, I can kindof buy that Go interfaces are often 80% of what you want.

Most of what I've been working on is a Rest API. Here's an example of a problem that felt like it should be something for which I'd be able to write an abstraction to deal with, but turned out to be challenging, at least as a n00b:

https://groups.google.com/forum/#!topic/golang-nuts/JhsiiGHQ...

(Incidentally, though I moved past my then really poor understanding of Go interfaces, I wasn't able to apply the respondent's answer or my new general knowledge to achieve the abstraction I was looking for, and settled for just grinding out the same code for the dozen entities/endpoints it applied to.)

Concurrency: I suspect there's a bit that's hidden in some libraries we're using, and we may yet need to write our own code for some API requests that get processed offline, but I haven't had to explicitly write code for it yet.

If you can think of a specific exercise that might serve to illustrate Go's outstanding concurrency strengths, I'd be interested to try it and compare efforts to another language or two.


(Incidentally, though I moved past my then really poor understanding of Go interfaces, I wasn't able to apply the respondent's answer or my new general knowledge to achieve the abstraction I was looking for, and settled for just grinding out the same code for the dozen entities/endpoints it applied to.)

I definitely can't deny that using interfaces instead of regular OO classes takes some getting used to.

Though I don't understand why you had to grind out duplicate code for all the entities / endpoints though. I hacked up things a bit to get it to run on the playground, but as Jesse McNelis wrote, just passing the pointer to foo worked for the code you wrote.

http://play.golang.org/p/jNpK5CRBfv


Regarding the sample code is "bson_*" is a bson code convention? I notice Mixed Caps [1]is the idiomatic code for go? TIA

[1]http://golang.org/doc/effective_go.html#mixed-caps


That stuff is from the bson Mongo package. I didn't want to pull it into this example, so that it could run on the playground. I replaced dots with underscores in that case.


It's a painful exercise (entirely due to Apple, not Go), but we implemented an Apple Push Notification Service provider in Go that relies on several of its concurrency primitives.

I think implementing a simple HTTP proxy might also be a really good exercise, though I haven't done it.


If you have spent six months, is that just occasionally tinkering and reading things online, or actually using it fairly regularly?

If you have developed software in go, and after a couple months you aren't finding its saving you time, or at least pleasant to work with, maybe its not for you.

I feel like it is similar to C, but more opinionated, with much less rope to hang yourself, and often things I would have used C or Java, I find doing them in golang saves me plethoras of time.

It is opinionated, but for me the aha moment was after I learned enough of the standard libraries to feel comfortable in the language, I find myself getting things done at lightning speed, and it has performance that is as solid as the jvm in your average use case.

I find the code is also pleasant to read, and the language lends itself to writing verbose, simple code, which I find easier to debug than overly abstracted systems.


> I find the code is also pleasant to read

This, to me, is what he's missing. On the continuum between readable and expressive, Go falls decidedly on the readable side. If you're working on a project by yourself for 6 months, you might not like Go. But if you work on a team or return to code you wrote 6 months ago and haven't touched since, that's when you'll appreciate Go.

Evaluating languages based on simple solo projects will always favor the more expressive languages, but it's a short-sighted evaluation.


Going by Paul Graham's "Blub" essay (http://www.paulgraham.com/avg.html), I'd say the problem is that languages like Go and Java are simply not high in the rankings when it comes to power.


The underlying assumption is that the problem that modern programming languages should solve is lack of power. But I don't see that problem in modern codebases. In my experience, the problem is complexity.


I think in this case more power might be a good proxy for less complexity: power should correlate positively with expressiveness, and expressiveness should correlate negatively with complexity. More expressiveness lets you build more powerful abstractions with less code.


> expressiveness should correlate negatively with complexity

I don't see why that would be true. In my experience, the most expressive languages often produce the most impenetrable spaghetti code. Can you explain your reasoning?


Consider e.g. traverse, which is a function you simply can't write in a language without higher-kinded types. Several times I've written half a page of code only to realize "that's just traverse" or "that's just foldMap" or so on. In a less expressive language I would've had to leave that half-page as is. Surely that means the expressiveness results in more readable, less complex code?


Sounds more like boilerplate or redundancy to me than complexity. Things I would call "complex" in a software system are god objects, APIs with too many options, invisible interactions between parts that appear unrelated, and so on.


Fair enough. In that case I'd say expressive languages help, but only indirectly, because they make it possible to keep track of effects and interactions without having to write reams of code.

In a language with lenses, you can delimit where your models are mutated. In a language that lets you treat composable effects in a generic way, you can e.g. split up your variables explicitly with ReaderWriterState, making it clearer which serves which purpose.

The kind of things you might be tempted to use "magic" or global variables for in a lesser language - the "cross-cutting concerns" - you can instead handle in normal code as a generic kind of context. E.g. an audit trail can just be Writer. Or actions that need access to a database session can just be another kind of context. I'm working on such a thing right now (see tpolecat's doobie for a public example of the same kind of thing) - whether a function accesses the database or not is visible right in its type, but it's easy to compose functions that do. The types ripple up, and in the end I'm probably going to do session-creation-in-view - but in a principled way, using the ordinary type system, visible in the code - with Spray, my web route definitions are just another ordinary bit of Scala code (whereas in most languages I would define routes in an external config file, or at best a config object a la Django - adding complexity).

Heck, error handling is a perfectly good example of this. In a less expressive language, the only way around the boilerplate of handling errors where they occur is unchecked exceptions (or effectively-unchecked exceptions, as in "throws Exception"). In a language with good generic context support, the possibility of failure is just another kind of context that we compose with the same generic tools as any other, and the control flow becomes something sensible again.


your "should" and "should" should be replaced with "doesn't" and "doesn't" or at best "may" and "may".

more code has been written in Go than Haskell, Rust and Ocaml combined.


More code will be written in COBOL this year alone than has ever been written in Go.


[flagged]


No, he's pretty right. Do you know what COBOL codebases are out there and what they power?


How many lines of Go do you think will be written this year?

50 million? 100 million?


So what? More code has been written in C and Java than Go and Rust and Ocaml and whatever-else combined. A language's popularity says little about its complexity or how good of a language it is.

Powerful languages let you grapple with problems, weak languages first make you grapple with the language before tackling problems. Some people feel like Go gets in the way. I have not written any Go, but I understand both the praise and the criticism.

Powerful languages can cause other issues, but they do enable certain things that weaker languages simply never can.


You're being a little vague. Can you give a couple examples where software projects were done in "powerful languages?" I can certainly appreciate that higher level languages are better. However, it seems like in practice they are not used as often as you would hope.


C and Java are not the subject here.


Do you have a source? I don't find your claim implausible, however I would like to know how you came to that conclusion.


the internet is my source.


The Internet, in general, can be very unreliable.


How do I know this statement is reliable?


> more code has been written in Go than Haskell, Rust and Ocaml combined.

[Citation needed]


> In my experience, the problem is complexity.

Simple languages led to complex code bases.

Many of the Enterprise Java sins were caused by the language limitations and developers trying to work around them.


I think the enterprise Java sins were caused by cargo cult programming. Java can actually be pretty nice when wielded by someone who isn't afraid to invent new things.


What leads you to believe the same won't happen to Go?

I have seen this happen in C codebases before Java took the enterprise.

Its own little macro based DSL and pointers that were actually handles for the real data.


Java's sin is a fetish for over abstraction. Go has a strong cultural emphasis on avoiding unnecessary abstraction.


Enterprise architecture is traversal to the languages being used.


True, but in a lot of enterprise situations the complexity is intractable - it is caused by business requirements.


Exactly because of that I rather use languages that provide the tools to work with complexity, instead of shunning it into the shoulders of developers.

Anyway Go has its use cases, and it is already an improvement if less C code gets written.


The problem with the Blub essay is precisely the assumption that all languages can be ranked on a one-dimensional axis called "power". That assumption is false. (I know this is heresy to say on HN, but in this essay, PG is simply wrong.)

I'm repeating myself here, but: A Haskell user and a Lisp user are both sure that they're at the top of the power curve, and they're both sure that they're looking down when they're looking at the other language, and they can both tell you precisely why they're sure they're looking down ("It doesn't even have a decent type system" and "It doesn't even have macros", respectively). But they can't both be looking down. What's wrong here?

What's wrong is the assumption that all languages can be ranked by a one-dimensional attribute called "power".

Or look at it another way: Power? Power for what? Well, for writing programs. But what kind of programs? General programs? I've never written a general program in my life; I've only written specific ones. Then "power" is whatever's going to make it easier to write the program I need to write. Whatever language does that is the "most powerful" language for the problem I'm facing.

What power is Go shooting for? It's the power to write a ten-million-line application and have it be maintainable for two or three decades. (That's not my guess; it's what Rob Pike said the language goals are.) If you face that kind of problem, Go may in fact be a very powerful language.


> So I'll ask: What is it I need to read/work through in order to at least "get" Go and really understand its strengths (whether or not I end up liking it)?

I highly recommend to read this article where Rob lays out the motivation behind many aspects of Go: http://talks.golang.org/2012/splash.article


Maybe unlike other respondents, I'm a Go amateur. I've written three projects in Go, one of a few thousand lines and two of a few hundred. Also unlike many others, I consider Go un-elegant to the point of being ugly. In other words, I'm no expert and no fanboy.

What makes Go my go-to language for certain kinds of projects (I'll doubtless be using it again) is that it gets shit done. It has the brutal simplicity and hardware proximity (and raw speed) of C combined with the small handful of things that most significantly contribute to make Java better than C: A simple type system, garbage collection, usable string handling and dynamic data structures (arrays and hash maps). Go also has a fairly large, useful library.

Multiple return values often including errors make error/exception handling very explicit and in-your-face. When something can go wrong, you the programmer are forced to recognize that and deal with it as soon as the function returns. That's a Good Thing IMO. Java-style exception handling, by comparison, looks to me like an elaborate language hack for passing the buck.

There's a ruthless kind of efficiency in Go's surrounding philosophy. There's one way to format Go code, you let the utility take care of it and never worry about formating again. The compiler won't tolerate various kinds of sloppiness, such as unused imports.

As somebody else mentioned, in Go, writing elegant code is not a consideration because it's nearly impossible. Well, I'm sure there are "better" and "worse" ways to write Go code, but not so obviously that I'd be tempted to give it a lot of thought. So I tend to just sit there and write code by the bucketful until I'm done.

Finally, the tooling gets the job done, and quickly. I can compile for and across various hardware environments and ship executables that don't rely on a JVM or other external dependencies.


What weaknesses of Pascal are being repeated in Go?


There is no escape. The language thinks it knows better than you. The standard library is allowed to do things that your code isn't.


Like what? The standard library is no more special, other than where it resides in the filesystem. You can write C and assembly code as well and link it to your Go code. And I'm not talking about cgo, I mean [568][ac], the Plan 9 C compiler and assembler that come with Go.


> Like what?

Maps, slices, the pre-defined functions.

More powerful languages make no big distinction between user defined types and the language provided ones.


I was thinking most obviously of generics. The go standard library is full of generic collections, but you're not allowed to use generics in your own code.


It looks like free pascal has generics: http://www.freepascal.org/docs-html/ref/refch8.html#refse46....

But to your larger point, doesn't any non-programmable programming language exhibit the same weakness to some extent?


Any language will have some "magic" things that are special-cased in the compiler - things like the basic integer datatypes, or the threading primitives - and probably a few more things that are implemented as C code (though if you're lucky this can be done in a "standard" way, through the language's FFI). But most of the standard library should be implemented in the language itself (look at e.g. the Java standard library, or the Python standard library. In a more coherent language like TCL, even language constructs may be written this way - e.g. "for" or "if" can be just ordinary methods. At the other extreme, PHP has the infamous T_PAAMAYIM_NEKUDOTAYIM, because a simple-seeming piece of syntax had to be special-cased in the parser). If the standard library authors kept feeling the need to step outside the language itself, that's a bad sign - it suggests the language is poorly suited to writing libraries in. But you're right that it's a question of degree rather than a binary thing.


That's not the standard library, though.


Actually, the c compiler just got deleted.

https://groups.google.com/forum/#!topic/golang-codereviews/A...


It may just not fit for you. I feel the same way about C++ -- I understand the strengths of a pay-for-what-you-use language with a ridiculously powerful template library, but it just never came naturally to hand for me.


Go is the only language that allows one to write very scalable TCP servers fast (i mean development effot). The servers are almost as fast as C (i.e. 15x Python), but the development time is like Python.


There are numerous options for writing very scalable TCP servers on the JVM e.g. Vertx.

Then you have the option of using Java, Clojure, Scala, Ruby, Python, Javascript or Groovy languages.


For the record, Go does not allow you to operate on fd, so some fine tuning TCP parameters does not happen unless you do reflection magic and use SysCall. Which is PITA compared to Python.


What about Julia?


Yeah, what about Julia?

which makes as much as sense as asking

- What about R? - What about GNU DAP? - What about FORTRAN?

for writing a TCP server.


Tooling, simplicity (eg. lack of features), composition, channels.


>People who seem to be smart nevertheless keep talking it up... some not even as just a good tool but as their favorite language.

Being good enough, not knowing any better PL-wise, and the Stockhold Syndrome explains that.


At launch, there was a flurry of attention. Google had produced a new programming language, and everyone was eager to check it out. Some programmers were turned off by Go's conservative feature set—at first glance they saw "nothing to see here"...

This was totally me. I am very much a programming language aficionado (or maybe just a dilettante), and when I first read about Go, I dismissed it quickly. I'd mostly been using Lua at the time, and didn't really understand what was different with goroutines vs. coroutines.

After the initial release, it took us a while to properly communicate the goals and design ethos behind Go. Rob Pike did so eloquently in his 2012 essay Go at Google: Language Design in the Service of Software Engineering and more personally in his blog post Less is exponentially more. Andrew Gerrand's Code that grows with grace (slides) and Go for Gophers (slides) give a more in-depth, technical take on Go's design philosophy.

It was Rob Pike's essay that caused me to investigate it again.

I have been quite impressed with lots of little details that have been 'fixed' (relative to C) in Go. Such as how variables are declared, the module system, and much more. And I was and continue to be impressed with the associated tooling.

I hope that if Go has just one lasting legacy in the history of programming, it will be how it pushed forward people's expectations of what a good language ecosystem should provide.


What do you think is worthwhile about Go? I agree that the tooling is nice, but beyond that, there is nothing interesting to me. Goroutines aren't interesting; languages like Erlang and Haskell got green threads right many years before Go was on the scene.


You should ponder why Erlang or Haskell achieved a fraction of Go's adoption despite being on the market 20+ years longer.

Some people see languages as a bag of features (immutability! generic programming! laziness! operator overloading! algebraic types! hindley-miller type inference! pattern matching! exceptions! manual memory management!). See http://yager.io/programming/go.html for an example of that line of thinking.

Those people won't get Go.

Designing a language is not about cramming every feature you can think of. It's about making good trade offs.

A trade off is: you get something but you also loose something.

I use Go because it made the biggest number of good trade offs.

Or to put it differently: I program in Go because when writing code, it irritates me less than other languages.

If you want a longer explanation of that: http://commandcenter.blogspot.com/2012/06/less-is-exponentia...


> You should ponder why Erlang or Haskell achieved a fraction of Go's adoption despite being on the market 20+ years longer.

This line of thinking serves more to call in to question the engineering and management cultures we have than it does to reflect poorly on Haskell and Erlang, and if it's true that Go's essential strength compared to them is that it is well-fitted to these cultures, that's not particularly flattering, however locally practical it may be.


That sounds almost like denial to me. "If everybody doesn't adopt what I think they should, it has to be because they're dysfunctional! It can't be because the stuff I like is less generally applicable than I think it is!"

If people disagree with you, your default assumption should be that they are looking at different evidence than you, not that they are incompetent or stupid.


If languages like Haskell and Erlang gave a competitive advantage, wouldn't we see companies which used them succeeding over those that don't?

Maybe Go is fitting into the cultures that succeed, and if that's the case, well it's the better choice, right?


> If languages like Haskell and Erlang gave a competitive advantage, wouldn't we see companies which used them succeeding over those that don't?

We are. WhatsApp generated a flurry of interest around Erlang. Heroku uses it. I'm sure there are more examples.

Google is at this point already a large corporation and probably already in decline (IMO). The tools they use are optimized for interchangeability of mediocre programmers, not for high productivity from a small team. Go is a perfectly good Java 1.4.

(There's Docker using Go, but I think their whole approach is a bad idea).


You are seeing companies that use those languages suceeding at a higher rate than those that don't? I bet I can find 10 succeeding that use ruby or node for everyone that uses Erlang.

We could also look at the open source world. For every riak there are 10 java sucesses of a similar kind.

We'd need to do some real statistics but my bet is that we see no benefit from those languages in terms of success of the business.


> I bet I can find 10 succeeding that use ruby or node for everyone that uses Erlang.

But that's not the question, is it? There are a lot of companies succeeding with ruby or node, but also a lot of companies failing with them. I'll bet the strike rate is better for Erlang companies.


Il bet that the choice of language has no impact on success.


What about whitespace? Do you think choosing it or brainfuck has an impact on success?


Not relevant. I'm sure there are bad choices, but the difference between Go and Haskell is probably negligible or in Go's favor.


So you think Go is a better language because companies that use Go are more likely to be successful because... Go is a better language? That's just circular reasoning. Do you have any numbers or experience here?

(In my limited experience I've seen a 100% success rate for companies that use Haskell and a 0% success rate for companies that use Go, though as you can imagine I don't have a statistically valid sample size)


Right, there is no good data at all suggesting that languages like Haskell are better for teams. Yet we are constantly served peoples overly confident opinions that they are.


We are? Links please.


As someone with experience in both languages I disagree. Try parsing json in both languages and you'll see a difference. Composing functions that use channels simply isn't possible in Go (please prove me wrong).

Go gives you kind of safe concurrency, Haskell gives you actual safe concurrency.


> If languages like Haskell and Erlang gave a competitive advantage, wouldn't we see companies which used them succeeding over those that don't?

Like these?

https://www.haskell.org/haskellwiki/Haskell_in_industry

https://www.erlang-solutions.com/industries

https://ocaml.org/learn/companies.html

http://fsharp.org/testimonials/


The stock price of Standard Chartered is 939 GBX currently.


I get the impression some of the folks in the Golang crowd are "Blub" programmers [1]. It's not so much a matter of simplicity and trade-offs as it is a matter of "I don't need things I don't know about", which isn't a good argument to use Golang.

[1] http://www.paulgraham.com/avg.html


Most Go programmers I know, including the language designers themselves, have a lot of experience in more feature rich and expressive languages. What appeals is the simplicity of Go, which gives you a lot less of a language to think about, so you can just think about solving problems instead.

“I like a lot of the design decisions they made in the [Go] language. Basically, I like all of them.” – Martin Odersky, creator of Scala.

I don't think anyone would accuse Odersky of being a "blub programmer."


Martin has something nice to say about everyone's project. I seriously doubt he likes that a new language was created in 2009 that allows null references. But we'll never hear about it if he doesn't. Still, that doesn't make him a Go programmer.


This is a great way to insult a bunch of people and start a fight.

"Beating the averages" is not a very good essay, I don't think you need to drag it in here.


"Blub" as a concept isn't really useful anymore. I've seen people attacked as "blub programmers" for criticizing Go for its lack of features. At worst it's a personal attack ("you are a short-sighted programmer"), at best it's kind of supercilious ("language X is great, it's just over your head").


I think "blub" is just another example of "you don't know what you don't know", which is always going to be relevant. And not everyone have tried, or intend on trying, obscure/sophisticated/mind-bending programming languages. And that's fine, since we all have different interests (as long as we don't try and pretend that we are very knowledgeable about something which we don't really have an interest in pursuing).

But to call someone a "blub programmer" does come off as quite rude.


I feel like you haven't met a lot of Go programmers in that case. The programmers I have met who use Go or are interested in it are often genuinely good programmers. Of course there are "crowd" followers in any language as popular in Go but Go is not a "Blub" language nor does it attract Blub programmers.

Do I as a Go programmer sometimes wish that Go had feature X. Of course I do! I want that feature when I want that feature. But, I find Go to occupy an extremely practical position in my personal programming language continuum do to its fairly unique mix of features. Note, it is not the features themselves that are unique many languages individually have them. Indeed, they often also include features I miss in Go. Rather, it is the particular mixture which is useful.

EDIT: To respond directly to the Blub article.

Features pg calls out for LISP:

    Garbage collection, introduced by Lisp in about 1960, is now widely
    considered to be a good thing. Runtime typing, ditto, is growing in
    popularity. Lexical closures, introduced by Lisp in the early 1970s, are
    now, just barely, on the radar screen. Macros, introduced by Lisp in the mid
    1960s, are still terra incognita.
 
Go score card:

    Garbage Collection [x]
    Runtime Typing [p]*
    Lexical Clusures [x]
    Macros [ ]

    * Go has some dynamic typing capabilities but nothing like Python or LISP.
      Many would consider that a "good thing". It partially depends on what you
      are doing.
Go has a 3/4 on the score card of features. Macros are of course enormously useful but also very difficult to shoe horn into a c-family language properly since they need to be expanded at compile time (unless you just go ahead and embed you compiler backend into the runtime system of your language. That would be kinda of crazy/awesome but you could do it). Rust has of course proven the utility of such a choice.

I can't speak for pg, but Go has many fantastic features and is not a Blub. The features which are missing are not, by and large, features of LISP.

EDIT 2: To add some more fuel to this fire...

pg concludes the article with:

    During the years we worked on Viaweb I read a lot of job descriptions. A new
    competitor seemed to emerge out of the woodwork every month or so. The first
    thing I would do, after checking to see if they had a live online demo, was
    look at their job listings. After a couple years of this I could tell which
    companies to worry about and which not to. The more of an IT flavor the job
    descriptions had, the less dangerous the company was. The safest kind were
    the ones that wanted Oracle experience. You never had to worry about those.
    You were also safe if they said they wanted C++ or Java developers. If they
    wanted Perl or Python programmers, that would be a bit frightening-- that's
    starting to sound like a company where the technical side, at least, is run
    by real hackers. If I had ever seen a job posting looking for Lisp hackers,
    I would have been really worried.
Note, he explicitly says here: not all languages are the same. He would worry when a company wanted Python programmers. Why? Because to him Python mixture of features represented a nice chunk of what LISP is providing him. Let's do another score card:

Python Score Card

    Garbage Collection [x]
    Runtime Typing [x]
    Lexical Clusures [x]
    Macros [ ]*

    * It is somewhat possible through black magic hackery to create an AST level
      macro in Python. It is messy. It is cool. It's kinda crunky. And I am not
      sure anyone has ever seriously used this ability. It certainly isn't main
      stream. Checkout https://github.com/lihaoyi/macropy for inspiration.
Norvig agrees with this assessment: http://norvig.com/python-lisp.html . Given that many people are ok with moving from Python to Go <https://www.reddit.com/r/golang/comments/2aup1g/why_are_peop... it seems reasonable to conclude that Go is an exceptible replacement for Python. Something no one has concluded about Java (for instance).

Therefore, I believe pg would have been equally worried or almost as concerned about Go programmers as Python programmers.


> Macros are of course enormously useful but also very difficult to shoe horn into a c-family language properly since they need to be expanded at compile time (unless you just go ahead and embed you compiler backend into the runtime system of your language. That would be kinda of crazy/awesome but you could do it). Rust has of course proven the utility of such a choice.

Rust doesn't do that. Rust expands the macros at compile-time. Integrating the whole compiler into the runtime wouldn't make much sense considering what area Rust is targeting.

It's also recommended to not link a crate that is used during runtime with the compiler or parser because they're fairly big and would bloat your final build by a large amount.


yep. I never meant to imply that rust macros were expanded at runtime. I was trying to use rust as an example of a c-style langauge with a nice macro system.


Actually, to be a little more constructive about this: I'd be really interested to hear from anyone who's used Go after doing a serious project in any of Haskell, Scala, F# or OCaml. All the Go advocacy I read seems to ignore that this language segment exists, and all the Go programmers I speak to seem to take a very "blub" position on features like pattern matching, ADTs, and the various things I'd summarize as "good type systems". So it would be great to hear from someone who's used these things seriously, understands their advantages, but still thinks Go is making the correct tradeoff.


I use Scala at work. I would rather write in Go than in Scala. While there are many things to like in Scala there are also many misfeatures such as the `implicit` key word which can create a lot of spooky action at a distance. Scala is an engineering tour de force and has many interesting ideas. However, I find that it has too many features and has a syntax with encourages obscure code. In contrast Go may be verbose at times but it is always very easy for me to read and follow.

I recently needed to dive into a legacy Scala code base which had not been worked on in a year. The engineer who had written it had moved on from the company and no one knew how it worked. The code was extremely difficult to read and reason about. Some of this was do to the programmer who wrote it but some of it was also do to language features. Is it possible write clear and concise Scala? Absolutely! But, the language does not necessarily encourage that style at this time.

Other functional languages I have used include: Scheme and SML. I want to check out OCaml next. I have also played around a bit with F* (not F#).


Languages without decent compile-time type systems are pg's own blub.


pg writes about macros as the top of the blub ladder whereas you mention compile-time type systems, but I'd say it isn't a ladder but a twin-peaked hill, i.e. strongly-typed lazy functional code (e.g. Haskell) and dynamically-typed variadic homoiconic code (Lisp). They have an "impedance mismatch" such that they don't inter-translate very well, unless they use clunky addons like Template Haskell macros or Typed Clojure annotations. The pure form of each language, however, is based on two mutually exclusive foundations, i.e. strongly typed auto-curried parameters vs variadic untyped macro parameters.

The strong typing and lazy evaluation of Haskell makes it easy for functions to take only one argument at a time. Although a function could take a tuple parameter, it is usually rewritten to take each component of the tuple as a separate parameter, which makes the strong typing and built-in currying simple, higher structures like monads possible, and a syntax to suit this style. Lisp functions and macros, on the other hand, must be variadic to enable the homoiconicity of the language. It's therefore much more difficult for parameters to be typed, or to curry them. The syntax requires explicit visual nesting.

The poster child of each style, monads and macros, are thus two peaks in language abstraction simply because of these different required foundations of each, and if you, lmm, have achieved Haskell-style enlightenment, then dynamic variadic homoiconic languages are your blub.


I use scala so I have both (somewhat clunky) macros and types. I am not unaware of the costs of clunkiness or the merits of the lisp approach, and I have not stopped looking for a language that can combine both advantages - in contrast to pg, who seems to dismiss the value of strong types without ever having used them seriously.


You're right, but don't assume that there are only two peaks. That's making the same mistake you're arguing against.


>Macros are of course enormously useful but also very difficult to shoe horn into a c-family language properly since they need to be expanded at compile time (unless you just go ahead and embed you compiler backend into the runtime system of your language.

No, that's not necessary at all, with Rust-like macros which are simple pattern->template things. Macros have zero runtime cost.


I totally agree I made this point badly.


Some people look at a new language and don't see anything that proves it was made after the 1970s.


And I hope it stays that way. We don't need more features, we need to address psychological issue of mistake-making.


> We don't need more features

You may be a victim of the Blub paradox. There are many useful and powerful features missing from languages like Go, and many of them (like powerful type systems) exactly address human mistake-making.


No need to get personal. I was talking about all the features in all the languages, not the ones in Go. And yes, we don't need more new features, we have plenty.

However, none of the languages I know of address psychological issues. Does type system reduce mistakes or introduce? Certainly both, don't know which one more though. But the question itself is wrong. It's not the type system who makes mistakes, humans do. And humans have brains. And brains are limited in their ability to do some thing while keeping other things in mind. And if among other things types happen to be present and mistake was made, than one could say types added to that mistake. And if types were not something programmer needed to think than type system certainly didn't add to that mistake. Things are not as simple as most programmers tend to think. Features don't matter how they think they matter. And research in language design was broken since the beginning of times and, sadly, still is.

(if you read "no silver bullet", "out of the tar pit" papers and couple of recent ones on bugs you should get the idea of how bad things are with mistake-making research)


How does a type system introduce mistakes? Unless it's unsound, of course (as is Java's and Dart's).


Java's type system is sound. There are even machine-verified proofs of this.


Oops, you're right. I thought you could override methods with more specific argument types, but it appears I was wrong.


well, rust has all those powerful features. it's been out in the open for exactly as long as Go has.


Rust isn't nearly as stable as Go. It still hasn't reached the state that Go was in when it was released outside Google. That's not a judgment of Rust, just a result of it being developed in the open instead of inside a company, and it being much more ambitious than Go.


Go was released as an open source project on the 10th of November 2009. Which part of that is _not_ open to you in a way that Rust is ?


Go seems to be an iteration of Rob Pike's previous languages, Limbo and Newsqueak - perhaps with a sprinkling of other ideas, but not much.

Rust on the other hand has been a far more ambitious project, with very lofty goals. This has meant that the Rust team has needed to do a huge amount of experimentation and iteration, culminating in the tight set of core semantics that you see in the language today. It's not been an easy journey for the them, but a great deal has been learned in the process, and even if Rust never 'makes it' into widespread usage, its contributions to the field of programming language design will be felt for years to come.

This is not to diminish the efforts of the Go team - they have produced a really tight, polished language, with an excellent set of bundled libraries. All I'm saying is that they weren't starting from scratch, and had different goals to the Rust team. That has meant they could get to a stable language much faster.


Rob Pike's keynote at GopherCon 2014 does a great walkthrough of the evolution of the design of the language, where ideas were borrowed from and why: https://www.youtube.com/watch?v=VoS7DsT1rdM


Oh nice, I'll check it out. It's always good to see where things come from.


Both of them are open now. In 2009, it was possible to write a guide to writing software in Go:

https://web.archive.org/web/20091113154825/http://golang.org...

That looks a lot like today's Go. All of the major design decisions were made, distinctive Go concepts (slices, maps, interfaces, goroutines, switching on concrete type, etc.) are all present. Most of the code examples would compile today with only a few tweaks (if any).

Suffice it to say that Rust was not in that state in 2010. It was barely in that state by 2014. Go was released as a beta; Rust was released as a pre-alpha. That isn't a judgment of either language or community.


Externally, it certainly seems that Go was open sourced in a far more finished state, with only relatively small tweaks after the initial release, while Rust was quite raw and has had most of its fundamental design work done in the open (with significant contribution from volunteers, both code and design) after being placed on github in 2010.


Rust 0.1 came out in the beginning of 2012, about 3 months before Go 1.0 game out (or go1). In the meantime, Rust has changed a lot and is only now close to a 1.0 release.


FWIW, Rust has been on github since June 2010.


rust was announced the same week Go was. it took a while to bring to the open, but it got the "swoon" demographic under its wing almost immediately.


Designing a language is not about cramming every feature you can think of. It's about making good trade offs.

Indeed. And the hallmark of the really good design is to examine the interactions of those features and see how well they mesh together. And what they imply about how actual programs are designed and maintained.


While I agree with these arguments, the purpose of Go is to Get Stuff Done (TM), and the main purpose of Haskell is to foster PL research and semi-formal methods. It's not really fair to say Haskell is unpopular because it crammed a bunch of things inthere: It is very popular in the niche it was designed for. Go just has a wider appeal.

And remember not to equate popularity with quality. We're all using JavaScript because that's what you need to do in the browser, not because it's a particularly good language. (Not that Go doesn't deserve the adoption it's gotten. It's great to see people ditching clunky Python/Ruby/C++/JavaScript stuff for Go.)


> I program in Go because when writing code, it irritates me less than other languages.

This is how I feel too. There are countless styles of prose, but some styles are restricted deliberately for aesthetics or practical reasons. I prefer to write prose in plain terms, and Go enables me to write code the same way. Some languages more than others have parts that stick out in odd ways, and add unwelcome personality to my code.

Yet sometimes I wish to put higher concepts into abstract terms, and Go is less expressive here. You can't always shape things the way you want, and you can't build reusable parts with the same flexibility as in other languages. But, when it comes to tinkering and giving form to ideas, I am more productive working with a basic set of LEGO than a box of all the specialized pieces someone may have thought I might ever find a use for.


> You should ponder why Erlang or Haskell achieved a fraction of Go's adoption despite being on the market 20+ years longer.

Because Google.

In longer form, because Google is big and Go is well-enough-adapted for problems Google has (there may or may not be better solutions, and there may be some NIH factor going on in Google favoring it, but its at least good enough), and because the fact that Google is big and behind Go, that gets it lots of attention and interest and use even in places where it may not be as well suited as alternatives or what it is replacing.


>You should ponder why Erlang or Haskell achieved a fraction of Go's adoption despite being on the market 20+ years longer.

Programmers liking confortable algol like syntax, and Google pushing it?


That's a reductive summary of goroutines. I don't doubt other languages have competitive features, but let's be clear about what the feature is:

* Lightweight threads with a scheduling and state overhead low enough to run hundreds of thousands of threads at a time.

* Seamless integration with the language, without any rituals or incantations needed to invoke them; you can, for instance, trivially pass closures from goroutine to goroutine.

* A data sharing scheme that makes sense with promiscuous threading ("synchronized" data structures often don't, because they'll end up serializing threads)

Lots of languages have green threads, but not all these properties.

I'm guessing Haskell does? I don't write Haskell, but my impression is that it has everything.


Erlang has the first two concepts nailed down.

I don't know what "data sharing ... with promiscuous threading" is, but Erlang is functional and immutable, so data sharing only exists in the sense of passing immutable data structures around. Synchronization issues associatied with mutations don't exist for in-memory structures since it's all read-only access.

I don't know Haskell's concurrency tools very well, but I believe it can provide much of the same functionality as Erlang, but the whole OTP toolset is missing — just as it is in Go. (Certain aspects of OTP can't be implemented in Go at all without additional changes to language/runtime.)


Neat. You can start an Erlang process (or whatever they're called) with an anonymous function, and pass it another anonymous function that closes over another lexical environment?


Yes, both in Haskell and Erlang.

For me, Go's big win is that I can finally bring this, the right way to do it, to work, because it's the first time this paradigm is present in a conventional imperative language, not that it is the first in general. Many other languages got it right in theory before Go, but there was always some practical stopper, involving some obscure programming paradigm that I stood no chance of getting buy-in on (from engineering or management) or an ecosystem that simply couldn't be reasonably be called production-ready across the set of tasks I needed to accomplish. Go finally gave me almost everything I wanted for work. If it isn't everything, well, that's life sometimes, you know?


Like Java's strategy (http://www.win.tue.nl/~evink/education/avp/pdf/feel-of-java....)

  Java is a blue collar language. It's not PhD thesis
  material but a language for a job.  Java feels very
  familiar to many different programmers because we
  preferred tried-and-tested things.


Absolutely:

    do_stuff() ->
      GetAnswer = fun() -> 42 end,
      spawn(fun() -> io:format("The answer is ~p\n", [GetAnswer()]) end).
Here, GetAnswer is a closure, as is the anonymous function given to spawn() function.


And here is why these functional langauges will never make main stream.

I've, been programming for 20+ years and I look at that code and say what the f*k?

Ooh, it's the functional stuff. Ok, move on.


Seriously? I mean, Erlang has some gnarly syntax, but this is really basic, and translates directly to anything you'll find in JavaScript, Go, Ruby, Scala, C++, even Java.

Let me take you through it:

    do_stuff() ->
This declares a function do_stuff(). It's exactly like:

    function do_stuff() { ... }
Everything after is the body. Unlike many languages, Erlang uses comma, not semicolons, as statement separators, function bodies end with a "." so a function follows the form:

    do_stuff() -> a, b, c.
Here, a, b and c are statements. The last statement provides the return value. This directly translates to:

    function do_stuff() { a; b; return c; }
Next line defines a variable:

    GetAnswer = fun() -> 42 end,
This just defines a variable which is an anonymous, inline function, sometimes called a closure or a lambda (all three are technically correct). In Erlang, anonymous functions end with "end", not ".". This is equivalent to JavaScript:

    var GetAnswer = function() { return 42; };
The next line is therefore easy to understand, as it uses the same inline function syntax; it calls spawn() with this function as an argument. So it's this:

    spawn(...)
The argument is this function:

    fun() -> io:format("The answer is ~p\n", [GetAnswer()]) end
JavaScript version:

    function() { return io.format(
      "The answer is ~p\n", [GetAnswer()]); }
(Of course, JS doesn't have spawn() or io.format(); this is just syntax.)

Complete version:

    function do_stuff() {
      var GetAnswer = function() { return 42; };     
      spawn(function() {
        io.format("The answer is ~p\n", [GetAnswer()]); });
    }
In some ways, the JavaScript version is actually gnarlier. Look at all those braces and semicolons.

The thing is, the syntax we found nice is usually nice because it's familiar. Many languages could seem like an incomprehensible mess if you're used to C-style brace syntax. But that's purely a question of familiarity. If you don't know what all the bits and pieces mean, you're going to be alienated by it. A developer who has grown up on COBOL and Forth will not find JavaScript syntax familiar any more than you find Erlang syntax familiar.

I suggest stepping out of your comfortable protective shell and trying it out. It's not rocket science. After 30-60 minutes reading this book you'll no longer find it alien, I bet:

http://learnyousomeerlang.com


Also don't ets tables do that, #3 I mean? Where different processes can access the same data structure? You can even control what process read or write to it. It is pretty neat.


Yeah, you could have said the same about Haskell. 'forkIO' in Haskell and 'go' in Go are essentially identical. You could also argue that Haskell's STM is even safer than using CSP channels (which also exist in Haskell) since you greatly decrease your chance of data races (while sacrificing some performance in cases where a transaction is often retried.)

But in the end it doesn't really matter that much. Go and Haskell are completely different languages for different purposes (getting stuff done in regular companies vs. doing interesting PL research or more advanced development in companies with little turnover.)


>You could also argue that Haskell's STM is even safer than using CSP channels (which also exist in Haskell) since you greatly decrease your chance of data races

We actually have transactional and non-transactional channels as well, which is pretty cool.

Also the reason for using STM instead of channels is that they compose without the possibility of deadlock. You can have deadlocks with channels.


I'll try to represent Haskell here.

1. Totally, definitely. The new Haskell IO manager in GHC 7.8 completely blows this out of the water. It's wonderful.

2. You fork or async IO procedures which can be considered immutable values in their own right and passed around without the slightest concern. There is a small difficulty in that Haskell's laziness makes it a small trick to ensure that work gets done in the right place, which is undoubtedly a mark against Haskell, though perhaps not a huge one.

3. Things like STM, MVars, and (now) LVish let this happen in all kinds of ways. The only problem might be picking the right option due to the wealth of choices. Fortunately, the right answer is almost always STM.

I'd be more than happy to translate a task to demonstrate these things.


> The new Haskell IO manager in GHC 7.8 completely blows this out of the water.

Go will still outperform Haskell by a huge margin and the typical Go process will have a much smaller memory footprint.

I don't even like Go, but nobody choosing Go is realistically considering Haskell as an alternative.


I encourage you to look a the benchmarks published here: http://haskell.cs.yale.edu/wp-content/uploads/2013/08/hask03... which includes Haskell beating nginx and Haskell beating SDN controllers written in C++ and in Java.


I'm not so sure that is true. I'd love to see some benchmarking, though.


I don't like go either, but I won't believe that a ghc webserver is a fast as a go webserver until we can see it in the techempower benchmarks.


The Techempower benchmarks are using the old version of GHC, 7.6, and the Golang devs have been tuning Go's core libraries to win at this specific benchmark.


Even then, it is only a factor of 2 slower. That is next to nothing.


Right, precisely, and this is at a very silly tier of microbenchmarking where you're doing little to no actual work, something you virtually won't ever find in the "real world".


> The Techempower benchmarks are using the old version of GHC, 7.6

7.4, last time I checked.


Clojure has all of this in core.async - which is just a library, it doesn't need special support from the underlying language.

goroutines are certainly nice and CSP is a good mental model for solving many problems, but I don't think a feature like this should be used to justify switching to a whole new language.


What? No. You could not be more wrong. core.async does not support the first point in the least.

Don't believe me? Make a go block in core.async and have it call another function, then have that function block. Do it 10,000 times. I'll wait. And wait. And wait. And wait...get the picture?

Clojure has this in Pulsar, which is a library...that happens to perform bytecode modification to support this (which I consider to be on the same level as special support from the underlying level). Pulsar is an absolutely amazing project and I'm not trying to take anything away from it with this comment, but merely trying to illustrate how far off you are.


This talk (mentioned in the article) captures a lot of the value that I see in Go: https://www.youtube.com/watch?v=dKGmK_Z1Zl0


What do you think is worthwhile about Go?

Simplicity - the main feature is all the interesting things they left out.


The difference is I can read Go code while having no experience with it. Erlang and Haskell are inscrutable to 90% of programmers.

If you think this isn't important, I will point out that Rust has been frantically trying to look more like Go and less like garbage in every release. Or, I will also point out that the Elixir project exists, which basically does the same thing for Erlang.


Rust is not "frantically trying to look more like Go". There are places where Rust's syntax deliberately differs from that of Go (for example, putting colons between values and types), and that will not change.


Go has a rather specific purpose. It's intended for writing server-side web systems that will run fast and scale well. Since that's what Google does to make money, that makes sense. The available libraries reflect this - good support for dealing with many network connections at once, no GUI support.

It's not suitable for writing an OS, hard real time, highly generic libraries, or GUI programs. Within its niche, it's far better than the alternative, which is usually C++. Just the fact that it eliminates buffer overflows without running slow is enough to justify it.

There are lots of problems with Go. The concurrency isn't safe. The lack of generics forces overuse of reflection and "interface{}", Go's all-purpose type. The lack of exceptions forces far too many lines of "if err != nil { return err}", (or worse, a goto) which takes 3 lines of text every time. The "defer" mechanism is clunky. Lots of modern bells and whistles, from functional programming to generators, were omitted. Other than the lack of safe concurrency, those things don't cause operational problems in your data center. They just require more typing by the programmers. That's an acceptable cost. It beats spending time in a debugger.


The lack of exceptions forces far too many lines of "if err != nil { return err}", (or worse, a goto) which takes 3 lines of text every time.

If I could go back in time, and discuss one thing with the designers, it would be to fix this.

I'd rather see some kind of Option type (like in Rust) baked deep into the language. Maybe there would be a scheme where you could use these Option types as regular values. The moment you try to use one of them that is actually an error (trying to pass it as an argument to another function without inspecting it first for example), it causes your current function to return an error.

Or something like that. Maybe that's a language design change that isn't going to be worked out in a social media post.

The "defer" mechanism is clunky.

I like defer. Open a file? Defer close it right afterwards in the code. Bam, done! That's nice, and works even if there was a panic deeper in the call stack somewhere.

The idiomatic way to do that in Go, however, can mask errors, like say if Close() returned an error, you might not see it if you just did a 'defer foobar.Close()' Those errors should go somewhere... somehow.


> The moment you try to use one of them that is actually an error (trying to pass it as an argument to another function without inspecting it first for example), it causes your current function to return an error.

I've seen another way to do it, that works with current Go, in a redis client [1]:

- Function 1 returns rawarg, error where rawarg can be anything

- You want to transform arg into some type, so you create a function that takes a rawarg and an error and returns your type and an error

- In the implementation of your 2nd function, if err != nil, return it directly

This way, as a library user you can just chain your calls without the tedious if err != nil (it will be taken care of in the library).

This might not scale to huge programs, but there certainly is a way to reuse this idea.

[1] https://github.com/garyburd/redigo/blob/master/redis/reply.g...


I'll have to take a look at that. Thanks for the link.


The idiomatic way to do that in Go, however, can mask errors, like say if Close() returned an error, you might not see it if you just did a 'defer foobar.Close()' Those errors should go somewhere... somehow.

You can, of course fix that, by wrapping your Close() in an anonymous function that logs an error if Close() had a problem. But I don't see that often in other people's code, and it is cumbersome. There ought to be a better and more concise way to do this.


Crazy that it has been 5 years. Time flies when your having fun.

The golang team has done a fantastic job. It is now my primary language of choice to get things done.

2 years ago I started playing with it moderately, and now in the past 12 months or so it has made its way into my normal workflow and have been delivering completed projects in golang.

I am excited for the next chapter in golang. Keep up the good work!


I've been using it instead of Python as my custom analysis tool - the ease of concurrency makes it very handy when working with large data sets, or gluing various codes together.


Where do you work where you get to pick so freely? What kind of projects?


I am a partner in a software consultancy company. We do any kind of unix based programming, and have worked on everything from financial software, mobile applications and streaming video servers.

Most of my work involves building server applications. I have deployed 4 golang apps this year. One is a pre-caching system that concurrently queries a bunch of mysql servers and caches data into redis. Another is a replication system from couchdb to postgresql, and the other two are slim restful api applications that sit in front of elastic search.


Shameless promotion in case you don't read OP. We're posting an article every day on blog.gopheracademy.com celebrating the use of Go around the world. http://blog.gopheracademy.com/birthday-bash-2014/go-turns-5/


The RSS feed of this Blog is broken :(


Thanks. Should be fixed now.


Go is an average language but it's a solid product.

The tooling is excellent. A lot of thougt has been put into making things simple. From the folder structure to the final binary, everything is smooth.

It's just a shame that the language design itself is quite behind the times. Older languages had a lot to offer to a new one. The day it launched it was already old. And I don't see a lot of goodwill to change the language to fill the glaring omissions.

Python in this respect is quite an example : iterators, generators, ABC were carefully added without making any less approachable. It prooves it is possible, but that the kind of things the Go leaders apparently simply don't consider.


You still need a Makefile if you use things like godep, or their new `go generate` stuff.

They have a long way to go on tooling; however, getting to say that is a luxury, due to just how "right" golang has been for systems work. Golang has been amazing to work with, and has just been stupidly productive. I miss debugging (gdb) and generic compile tools like tup, but that's about it!


I saw a debugger[0] posted on the go-nuts mailing list today[1]. It's fairly simple at this point, but it works.

[0] https://github.com/derekparker/delve

[1] https://groups.google.com/forum/#!topic/golang-nuts/bmsFE3dQ...


Thank you for mentioning it. With the last version of liteIde (refactoring, jump-to, usages, info about a anything...) the only thing that I missed was a debugger. I Hope that the project gains traction.


I don't know why people hate on Makefiles, make is great! Does exactly what it says on the tin.


I don't hate Makefiles, in fact they're quite wonderful in languages that have file based build systems (see: C). I'm just tired of the go team touting that they are unnecessary.. except that every major go project has one (or something like make.go in camlistore).


Do people prefer not using Makefiles? I ask because Makefiles are a standard part of my workflow, and while I know I can 'go build' or 'go [whatever]' I still cling to my Makefiles as a preferred tool.


I like make, and get a kick out of possible interesting successors, like tup and redo. I forced myself to stick to "go build" when starting out with go, and I'm glad I did...more time working on what I'm working on, less time fiddling with the build system. I'd recommend it, at least until you really hit its limitations.


I'm digging into cmake, because it's the one supported by clion, and I don't think I'm going back to make.


You don't need a Makefile for either of those.


I do if I want to encode how people who consume a pile of code either add or remove dependencies, or want to edit/recompile code that was generated. And in fact, especially at my job, it's literally what I want everyone to do in a consistent way.

A convenient way to do that is ask people to type 'make'.


Very awesome. I'm so proud to have been an early committer! Unfortunately, I don't use Go that much day-to-day, but here's hoping for another 5 years!


Does anyone know how Go is used inside Microsoft, except their participation in the Kubernetes project?


I expect that Microsoft will be using Go for their Docker integration.


Why are language discussions so futile? Because people choose platforms, not languages. Platforms have one main language (in some cases two) which becomes the language of (no) choice for the developers.

Platform examples: Host (Cobol, PL/1), Unix (C), Embedded (C), classic Windows (C++, VB), .NET (C#, VB.NET), Java EE (Java), Android (Java), ... Rails (Ruby), PHP (PHP), Browser (JavaScript).

The choice is always between platforms, not between languages. Languages without linking to a platform (Go, Python, Scala, D, Rust, ...) have little chance to succeed.


I don't see how PHP is a platform like the other platforms you mention. Also Python is very successful, Perl is very successful, Debian was very successful in its time. Your argument does not seem to be corroborated by facts.


Because people choose platforms, not languages.

No, because a language is not (just) a technology, it's a community, and as such, people invest their identities in it. If possible you make an enlightened choice about joining a community that shares your ideas and values and is doing similar kinds of work to you, so they are producing libraries and documentation and hosting relevant and interesting conferences. I often feel that people who lash out at other languages, are people who have come to regret their own choice of community.


Writing in Go has made me find Python code ugly. Much like Python made me dislike Perl many years ago. That is, of course, very subjective.


Sometimes I wonder: if Apple's new language had been Go, and Google's new language had been Swift, how would reaction by developers and adoption have differed? (You can also run this thought exercise with Go and Rust, if you prefer a different competition between self-proclaimed systems languages.)


I'm not totally sure how a non-optionally garbage-collected language with a runtime could ever be called a "systems language."


The "systems" in the term "systems language" the Go team used shortly after initially releasing Go refers to systems like Google has them running.


Half a decade sounds better than 5 years.


How I wish Gilad Bracha was in Go team instead of that js replacement language nobody is using...


I would really like to see go binaries that is not like a 1mb in size.

In practice is not like I don't have space for 1MB

But I still want proper linking to a shared "golib"


Binary sizes are a concern, particularly with a move to mobile devices and later embedded systems.

However for Go's current platforms, I would call it a non-issue. Static builds, something seemingly long forgotten though supported in other compiled languages, make deployment and distribution that bit easier.

I would support static builds by default, optional dynamic builds.


Only Go developers install the Go toolchain, so there's very little chance that your executable will work where you intend it to.


I really dislike the syntax. The language itself has nice concepts.


What precisely is it about Go's syntax that you dislike?


> , we were calling Go a "systems language"

And maybe in another 5 years, people will stop bickering over whether that description is/was appropriate. :)


Dare to dream big!


If only people would not willfully ignore the implicit "at Google" or "for Google" that a lot of things said about Go carry. Go may be a terrible language from the PLT POV, but it's meant to solve problems Google has.


They won't stop, they will only pause... so that they can spend some time bickering over whether or not lack of generics makes Go unusable.


A full lustrum.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: