I did Go for years, but stopped doing any serious work in it about a year ago.
In general, I found it a chore to maintain Go-based systems.
There's a lot to like about Go. But it doesn't seem pragmatic for the types of applications I see people using it for.
For example, the entire error thing is absurd. Anders Hejlsberg got this right many years ago: 9 out of 10 errors are "handled" by a central error handler (log + "sorry, try again"). You can do this with error values, but at best it's verbose. Maybe error values makes sense for system programming, I could certainly believe it makes more sense.
But, really, the main problem is the type system. I'm a huge fan of dynamic languages. I think they're so much more productive. This is especially true when your system is interacting with external data - like user input and a database. I did C# and Java for years. These interactions are, at best, painful with static languages. But they're downright infuriating in Go (poor reflection, no generics). I wrote a somewhat popular library to help deal with map[string]interface{} nonsense (2), but it's not enough.
When it comes to web/tcp stuff, I think OpenResty for any middleware-ish system and Elixir for everything else are far more productive and enjoyable ecosystems. We've been doing Elixir for about a year now, and I've never seen such a consensus on productivity and quality (everything else always had some issue: Ruby is slow, Node is single threaded, Java is verbose).
There's certainly more space than just that. Go might still be a good choice for CLI tools, and both OpenResty and Elixir have a distinct memory/threading model that precludes some use cases - but those are in the minority.
(1) - Nothing against Erlang model, but I do think it's more restrictive.
> This is especially true when your system is interacting with external data - like user input and a database. I did C# and Java for years. These interactions are, at best, painful with static languages.
Why? I find when you're importing external data or user input, that's exactly where you want strong types as that's the most likely place unexpected values are going to be generated (e.g. unexpected null values, string instead of an array).
I don't like the "central exception handler" idea -- it is invariably too far away from the error site to do anything intelligent.
I do like Erlang's approach with hierarchical error domains ("processes" and "supervisor trees"), and I honestly think Go made a mistake here: The semi-recent introduction of Context acknowledges that a control hierarchy is present and an important part of a concurrent, distributed application and that there are aspects of this that need to flow through everything. The Context exists to permit a caller to call functions and launch goroutines that inherit deadlines, the ability to cancel their work, and so on. But this is a rather blunt and intrusive tool that could have been better solved by baking termination into the goroutine scheduling runtime. Of course, one reason goroutines cannot be forcibly terminated is because everything is mutable all over the place, but you could still have termination induce system-level errors that the goroutine would be forced to handle (or maybe you could safely induce a panic)... but that ship seems to have sailed a long time ago.
And yet a hierarchical approach similar to supervisor trees would have been awesome to have in Go. Managing hordes of complex, "crash only" goroutines in Go gets gnarly fast, and you tend to end up with your own ad-hoc type of supervisor trees that uses waitgroups and maps and what not to corral trees of composed workers to ensure they are all able to cancel, retry + back off, and time out appropriately. Even something like retry with backoff isn't part of the standard library, even though it's extremely core to distributed, concurrent programming.
I think I agree with Go's authors that exceptions are bad, but exceptions in an explicitly hierarchical, supervisor-type process model, where processes are "crash only", actually make sense. It means you tend to design your system into discrete parts consisting of dumb workers (that give up and crash on the smallest error) and slightly less dumb supervisors (that react to said crashes). It's not a panacea, but it's better than the loose, somewhat impoverished set of tools that Go gives you.
>For example, the entire error thing is absurd. Anders Hejlsberg got this right many years ago: 9 out of 10 errors are "handled" by a central error handler (log + "sorry, try again").
You are correct that this is how it's often done - 9 out of 10 errors probably ARE "handled" by a central error handler. But this is an incorrect approach if the goal is a reduction of catastrophic errors.
In the following paper, "Simple Testing Can Prevent Most Critical Failures" (OSDI '14). the authors discuss how some catastrophic errors occur in distributed systems that they test (Cassandra, HBase, Redis, HDFS, MapReduce).
Notably section 4.1: Trivial Mistakes in Error Handlers:
"""
Figure 5 further breaks down the mistakes into three categories: (i) the error handler ignores explicit errors; (ii) the error handler over-catches an exception and aborts the system; and (iii) the error handler contains “TODO” or “FIXME” in the comment.
25% of the catastrophic failures were caused by ignoring explicit errors (an error handler that only logs the error is also considered as ignoring the error). For systems written in Java, the exceptions were all explicitly thrown, whereas in Redis they were system call error returns.
"""
I haven't used Go much but I personally much prefer Rust's error handling which appears to be largely similar.
I've done my first project with go. Goroutines are undoubtedly cool and I like the strong typing.
* Unfortunately error handling sucks (no stacktrace by default? Let's go back to the days of peppering print statements around just for fun).
* DB support is terrible (I've spent hours doing what I could have done in half an hour with python).
* It's so verbose, even simple things like mapping over an array to create another array requires a for-loop dance. Same for just checking if a value is in an array. Same, obviously for no exceptions which you either love or hate. I hate.
I can't see myself doing any other projects with it.
Please, nobody write full fledged WebApps or all of your middleware in lua. Trust me that's a horrible idea. I have seen about everything you can do in lua/nginx and it does not turn out pretty.
> I'm a huge fan of dynamic languages. I think they're so much more productive.
This hasn't been my experience. I work in Python (and JS) by day, and I can't wait until we upgrade to 3.6 so I can reasonably employ type annotations. Without them, people tend to write hacky code, such as functions which do completely different things based on the data type passed in (rather than fixing the abstraction) or build up huge, nested data structures with no formal definition (various parts of the code base just tack on whatever they like, and the documentation inevitably falls out of date). Even my prototyping is in Go, because I would rather have a compiler catch my errors than to spend tenfold the time trying to debug. Also, when I'm working in Python, I sorely miss the tooling that I have in Go and other static languages.
I don't think Go is the end-all-be-all of programming languages, but I think any dynamic language would be more productive if it was at least optionally typed.
"I'm a huge fan of dynamic languages. I think they're so much more productive. This is especially true when your system is interacting with external data - like user input and a database."
Im not so sure about it. Database is not dynamic and user input should be controlled, or number, or date, or text, for example an user could enters the price of a product a text.
I always wonder if the Go error issues could be handled more cleanly by using the suture library. I haven't seen widespread use but it's supposed to give a supervisor pattern to Go.
The Node event loop (the control plane) is single threaded, but the data plane is multi-threaded, you can easily write C++ bindings to run in the thread pool to handle CPU intensive work.
My opinion is the opposite. Almost every go code base I've seen is extremely consistent compared to other languages. Going from one code base to another is almost always seamless because learning Go involves learning the Go tools which forces you to follow Go coding standards. I'd choose to work on a Go code base over Java or C++ any day of the week.
I don't have much experience with elixir, but that's because I like to stay employed.
>the entire error thing is absurd. Anders Hejlsberg got this right many years ago: 9 out of 10 errors are "handled" by a central error handler
This is almost always unacceptable in production, especially when it comes to mission critical hard/software. You need to be able to handle errors in production that you may not even be able to catch in testing and that means you need to catch all the errors and think critically about what should be done when that error is encountered. "Central error checkers" are almost never robust enough for mission critical systems.
Go basically forces people to write high-quality, resilient code bases, and I like it for that.
> You know how much code I’d have to write if this were C++, C#, or Java? None. They all have reusable notions of an immutable, ordered map.
Actually C++ doesn't have immutable data unless you count compile-time constants and literals. You can declare things 'const', but that only provides a read-only (1) view on mutable data.
Also, to provide good 'const' support in containers, you usually have to provide extra read-only iterators that are largely identical to the writeable iterators. Go look at the C++ standard library. All the containers have both 'iterator' and 'const_iterator' (not just a 'const iterator').
Another approach is to just declare member variables 'const'. So you have a 'const map<int, int>' as a member variable. Except for that to work, you need to construct that member variable in the initializer list. The body of the constructor is too late. This likely means taking an already-initialized map as a constructor argument and copying it (probably exposing implementation details in the process) or writing a helper that returns the appropriate map type and copying it. Good optimizers will elide many or all of these copies, but relying on quality of optimizers isn't portable.
Anyway, I'd say the complexity of the Go approach isn't significantly worse that the C++ alternatives, if it's worse at all.
(1) Of course, with casting shenanigans or use of the 'mutable' keyword, anyone can circumvent 'const' without a lot of work.
> Actually C++ doesn't have immutable data unless you count compile-time constants and literals. You can declare things 'const', but that only provides a read-only (1) view on mutable data.
const std::set<int>& s // This is a read-only view.
const std::set<int> s // This is immutable.
> Another approach is to just declare member variables 'const'. So you have a 'const map<int, int>' as a member variable. Except for that to work, you need to construct that member variable in the initializer list. The body of the constructor is too late. This likely means taking an already-initialized map as a constructor argument and copying it (probably exposing implementation details in the process) or writing a helper that returns the appropriate map type and copying it. Good optimizers will elide many or all of these copies, but relying on quality of optimizers isn't portable.
Foo() : s(helper()) {} // This is a move, not a copy.
Yes, if you use the escape hatch from the type system, the type system won't help you. Do you also avoid the use of strong types since you can always cast to a *char?
So, once upon a time I wanted to associate information with http connections (as I am accustomed to in every other language I've ever written) just to enable proper http keepalive and debugging through a proxy written in Go.
Ended up with this. [0]
Turns out the Go authors don't think you should do this so I had to majorly alter and recompile the stdlib.
I still appreciate many go tools and the cross platform single binaries that work great on Solaris/Illumos. But in many cases the strong opinions of Go authors really stand in the way of getting work done.
That's more a criticism of the standard HTTP server, which is a bit of a black box, apparently intentionally. There's nothing wrong with writing your own server to make up for it's limitations.
I left it a while back, with a lot of the same frustrations. But then I tried a bunch of other things, and nothing seemed to work better. There seem to always be some frustrations with whatever language. And I found myself craving that simplicity again.
I might get annoyed with it again, and try some other stuff. And that might stick. But I suspect Go is the worst language except for all the others [1].
I did the back-and-forth for several years with Go. What drew me in was promises and wishes, what made me leave every time was realizing I had fallen for the same stupid trick again. It only looks simple because of the features they skipped and the arbitrary limitations they decided on. Once a codebase reaches a certain level of complexity and the need for more powerful abstractions arises, the perceived simplicity is no more.
I think you have to work with the language you're in. I had similar frustrations when I first moved into doing some dev in Java and C# and it really bugged me that there was no compile-time guarantee that an Object reference passed as a method parameter would not be modified within that method (as I was so used to using const-refs with in C++)
In retrospect however, I was frustrated with these languages because I was wanting it to be something it wasn't. To my mind, the loss of that feature meant that programs were less expressive - particularly in large codebases - I had to read methods to work out if something was being modified or not.
To complain here about Go lacking immutability and constness features seems to be the same as complaining that it is missing in Python, Ruby or JavaScript. It's a fact that constness isn't a feature of these languages, if you want these features, use a different language.
Sometimes it doesn't cost much if anything to add a feature which improves (type) safety. Now that Python has some type annotation support, it could make sense to also add a const annotation.
Go should have had it from the beginning IMO, immutability can help avoid some coding mistakes and also makes it easier to reason about code. It'a nice match for a language with static typing.
I don't see what's wrong with venting frustrations with particular technologies. It helps no one if people just move on silently.
It deprives the creators of that technology of the opportunity to react to any complaints and it deprives everyone else of the opportunity to choose a different technology from the start.
The link to the immutable, ordered map that "Java provides" seems to be a link to a third-party library created by Google.
If that's enough for you to consider that a language provides a feature, just wrap the immutable ordered map implementation in your blog post in a library, publish it, and now Go provides that functionality too.
After a year of programming with Go I have noticed that I, an old C++ programmer, have the reflex to decompose a program in terms of interacting data structures (objects) which encapsulate their behavior. I programmed like that in Go in the beginning. But when I looked at other Go code and especially the std lib code which is easily accessible, I was always amazed at the simplicity of the code. Now I try to write simple programs and this is hard after years of C++ programming.
I don't know if there is a term for Go like programming. I'm tempted to call it minimalism. It may be uncomfortable for people coming from classical programming, but the simplicity of the language and the code is a clear win in the long run.
It's so interesting how the hype cycle works. Only 1 year ago, people on HN were bashing Node.js hard and saying that you should use Go instead and everyone was praising Go.
Now it seems everyone is bashing Go. Moral of the story; never buy into the hype whether it is positive or negative. I think that Go is just fine and so is Node.js.
Maybe the author of the article makes a valid point, but no language is perfect and ultimately you have to understand it well in order to use it well.
I've been reading HN for a couple years now and can't recall there ever being a consensus positive opinion about Go. All the discussions pretty much looked like this one.
Of course the problem he has is that it doesn't follow the hot new immutability fad, and of course he doesn't explain what he needs it for, just links to a stack exchange question which essentially says that it might be useful for some cases.
It seems a lot of these articles, and programming language theory in general, is just complaining about features without any thought as to why they matter. What is this "problem" stopping him from doing? Why does it matter? And why should I abandon a language that has served me very well just because some edge-case feature is not supported?
Spot on. As a card-carrying dinosaur I've found myself from time to time needing to read up on some "new" (usually turns out to have been invented in the 60's) coding thing.
Once I figure out what it is, I ask myself what problem it solves (the literature typically doesn't say). The answer tends to be one of:
1. Saves some typing.
2. Saves some work when refactoring.
3. Avoids some class of bug.
4. Highly useful in a kind of programming I don't do (e.g. compilers).
#1-2 in my experience are much more common than #3-4 and often #1-2 can be dealt with through tooling (IDEs for example).
Worth reading the comments where someone points out a much simpler and more elegant solution, although it's still not an ideal situation.
As noted in the post, generics could solve this, so I'm looking forward to Go 2 if indeed generics is included. There's a few QOL issues I'd like to see addressed in 2 which would make Go a lot nicer to work with, and I say that as someone who spends most of their time working on Go codebases.
> Worth reading the comments where someone points out a much simpler and more elegant solution, although it's still not an ideal situation.
I'm always wary of such "<language X> isn't great because..." posts as I find that the frustration is as much a function of the developer's competence, (lack of) imagination and familiarity with the language and its idioms, as it is with the language itself.
Go's been great for creating a web scraper. But it's been an unmitigated disaster for being able to write the data to a database due to the immaturity of ORMs.
Might as well not pretend something is an ORM if it can't do the fundamentals (like upserts), fully hydrating structs, etc.
Having to encapsulate data to ensure read only semantics seems like par for the course from an OO perspective. In C# (which is 15ish years old at least) I don't think there has been an immutable map until very recently. Also (and this is probably a code style thing) I can't remember having reached for an immutable ordered map in a long career.
> You know how much code I’d have to write if this were C++, C#, or Java? None. They all have reusable notions of an immutable, ordered map.
Java doesn't do that well at all though; its immutable maps can be used in place of mutable maps, since there's no way of having a Map interface that extends ImmutableMap to add extra operations, so you can get nasty runtime surprises if you've got the wrong one. Conversely, if you want a mutable map keyed on a non-trivial structure, you have to do just as much boilerplate as the author complains about to set up private members, setters, builders, etc.
Obviously C++ can do it quite readily, but nobody accused it of lacking features.
This is really just another use case for generics, and the discussion there is well-trodden. Go probably isn't the language for you if you value generics more highly than fast compile times, native binaries, etc, but I'm not sure why that still seems to come as a surprise.
I am not sure where the authors exact requirements fit in the greater picture of the desired application. But one important tool which works great in Go is functional representation. If you want e.g. an immutable map, make it a structure with private fields, and give access vial functions (methods). The first comment to the article describes a nice example of this approach.
I often read criticism of Go which is based on a missing "feature". In my experience, implementing these features is surprisingly easy in Go, as the building blocks are all there. In this respect, Go has some resemblance on Lego :).
My background contains a lot of Scheme/Lisp, so abstracting things with functions comes natural to me. It turns out that Go has basically the equivalent function concept (first class functions, closures, ..) so enables all the powerful abstractions coming from them.
The author discounts this as a good solution because you'd have to re-implement the structure and functions for every single type you might put in the map; i.e. that would be a valid solution iff Go supported generics, but it does not.
OT: I recently learned that the newer ETH client Parity is written in Rust and much faster than the older Go-based Geth ETH client.
Guess it is due to a different architecture and design which makes the client faster (e.g. chaindata is smaller) but does anyone know why they chose Rust instead of Go for the Parity client?
I personally tend to choose Go for newer projects as I'm looking for the efficiency of C and safety/elegance of more "modern" languages.
However, I always keep an eye on D and Nim. It seems these are the only two languages that can compete with Go in both above aspects.
I asked this same question on reddit. And I am not trying to be snarky. I have programmed a long time and I cannot think of a single time where I thought, "this would be easier with generics."
Where is the list of problems that are easier to solve with generics?
The OP suggests "some form of codegen" as a way of getting around the Go dev team's, shall we say ... unusual hostility toward generics. But that way lies C++ templates, which were originally meant to be implemented as macro-generation -- and which lead to the messiness and complexity of C++ builds which were one of their major reasons for doing a new language in the first place.
It's an interesting point of view that regards those four items as necessary requirements for a data structure, and then regards the 24 lines of code needed to implement it as a massive "explosion of code." It would be interesting to know what language is considered the ideal tool for someone who looks at things that way. Something functional?
[+] [-] latch|8 years ago|reply
In general, I found it a chore to maintain Go-based systems.
There's a lot to like about Go. But it doesn't seem pragmatic for the types of applications I see people using it for.
For example, the entire error thing is absurd. Anders Hejlsberg got this right many years ago: 9 out of 10 errors are "handled" by a central error handler (log + "sorry, try again"). You can do this with error values, but at best it's verbose. Maybe error values makes sense for system programming, I could certainly believe it makes more sense.
But, really, the main problem is the type system. I'm a huge fan of dynamic languages. I think they're so much more productive. This is especially true when your system is interacting with external data - like user input and a database. I did C# and Java for years. These interactions are, at best, painful with static languages. But they're downright infuriating in Go (poor reflection, no generics). I wrote a somewhat popular library to help deal with map[string]interface{} nonsense (2), but it's not enough.
When it comes to web/tcp stuff, I think OpenResty for any middleware-ish system and Elixir for everything else are far more productive and enjoyable ecosystems. We've been doing Elixir for about a year now, and I've never seen such a consensus on productivity and quality (everything else always had some issue: Ruby is slow, Node is single threaded, Java is verbose).
There's certainly more space than just that. Go might still be a good choice for CLI tools, and both OpenResty and Elixir have a distinct memory/threading model that precludes some use cases - but those are in the minority.
(1) - Nothing against Erlang model, but I do think it's more restrictive.
(2) - https://github.com/karlseguin/typed
[+] [-] seanwilson|8 years ago|reply
Why? I find when you're importing external data or user input, that's exactly where you want strong types as that's the most likely place unexpected values are going to be generated (e.g. unexpected null values, string instead of an array).
[+] [-] atombender|8 years ago|reply
I do like Erlang's approach with hierarchical error domains ("processes" and "supervisor trees"), and I honestly think Go made a mistake here: The semi-recent introduction of Context acknowledges that a control hierarchy is present and an important part of a concurrent, distributed application and that there are aspects of this that need to flow through everything. The Context exists to permit a caller to call functions and launch goroutines that inherit deadlines, the ability to cancel their work, and so on. But this is a rather blunt and intrusive tool that could have been better solved by baking termination into the goroutine scheduling runtime. Of course, one reason goroutines cannot be forcibly terminated is because everything is mutable all over the place, but you could still have termination induce system-level errors that the goroutine would be forced to handle (or maybe you could safely induce a panic)... but that ship seems to have sailed a long time ago.
And yet a hierarchical approach similar to supervisor trees would have been awesome to have in Go. Managing hordes of complex, "crash only" goroutines in Go gets gnarly fast, and you tend to end up with your own ad-hoc type of supervisor trees that uses waitgroups and maps and what not to corral trees of composed workers to ensure they are all able to cancel, retry + back off, and time out appropriately. Even something like retry with backoff isn't part of the standard library, even though it's extremely core to distributed, concurrent programming.
I think I agree with Go's authors that exceptions are bad, but exceptions in an explicitly hierarchical, supervisor-type process model, where processes are "crash only", actually make sense. It means you tend to design your system into discrete parts consisting of dumb workers (that give up and crash on the smallest error) and slightly less dumb supervisors (that react to said crashes). It's not a panacea, but it's better than the loose, somewhat impoverished set of tools that Go gives you.
[+] [-] fnord123|8 years ago|reply
You are correct that this is how it's often done - 9 out of 10 errors probably ARE "handled" by a central error handler. But this is an incorrect approach if the goal is a reduction of catastrophic errors.
In the following paper, "Simple Testing Can Prevent Most Critical Failures" (OSDI '14). the authors discuss how some catastrophic errors occur in distributed systems that they test (Cassandra, HBase, Redis, HDFS, MapReduce).
http://www.eecg.toronto.edu/~yuan/papers/failure_analysis_os...
Notably section 4.1: Trivial Mistakes in Error Handlers:
"""
Figure 5 further breaks down the mistakes into three categories: (i) the error handler ignores explicit errors; (ii) the error handler over-catches an exception and aborts the system; and (iii) the error handler contains “TODO” or “FIXME” in the comment.
25% of the catastrophic failures were caused by ignoring explicit errors (an error handler that only logs the error is also considered as ignoring the error). For systems written in Java, the exceptions were all explicitly thrown, whereas in Redis they were system call error returns.
"""
I haven't used Go much but I personally much prefer Rust's error handling which appears to be largely similar.
[+] [-] brango|8 years ago|reply
* Unfortunately error handling sucks (no stacktrace by default? Let's go back to the days of peppering print statements around just for fun).
* DB support is terrible (I've spent hours doing what I could have done in half an hour with python).
* It's so verbose, even simple things like mapping over an array to create another array requires a for-loop dance. Same for just checking if a value is in an array. Same, obviously for no exceptions which you either love or hate. I hate.
I can't see myself doing any other projects with it.
[+] [-] ejcx|8 years ago|reply
Please, nobody write full fledged WebApps or all of your middleware in lua. Trust me that's a horrible idea. I have seen about everything you can do in lua/nginx and it does not turn out pretty.
[+] [-] weberc2|8 years ago|reply
This hasn't been my experience. I work in Python (and JS) by day, and I can't wait until we upgrade to 3.6 so I can reasonably employ type annotations. Without them, people tend to write hacky code, such as functions which do completely different things based on the data type passed in (rather than fixing the abstraction) or build up huge, nested data structures with no formal definition (various parts of the code base just tack on whatever they like, and the documentation inevitably falls out of date). Even my prototyping is in Go, because I would rather have a compiler catch my errors than to spend tenfold the time trying to debug. Also, when I'm working in Python, I sorely miss the tooling that I have in Go and other static languages.
I don't think Go is the end-all-be-all of programming languages, but I think any dynamic language would be more productive if it was at least optionally typed.
[+] [-] jorgec|8 years ago|reply
[+] [-] vince14|8 years ago|reply
You can still utilize all of your cores in Nodejs. So which problem are you encountering?
[+] [-] brightball|8 years ago|reply
[+] [-] _urga|8 years ago|reply
[+] [-] 43224gg252|8 years ago|reply
My opinion is the opposite. Almost every go code base I've seen is extremely consistent compared to other languages. Going from one code base to another is almost always seamless because learning Go involves learning the Go tools which forces you to follow Go coding standards. I'd choose to work on a Go code base over Java or C++ any day of the week.
I don't have much experience with elixir, but that's because I like to stay employed.
>the entire error thing is absurd. Anders Hejlsberg got this right many years ago: 9 out of 10 errors are "handled" by a central error handler
This is almost always unacceptable in production, especially when it comes to mission critical hard/software. You need to be able to handle errors in production that you may not even be able to catch in testing and that means you need to catch all the errors and think critically about what should be done when that error is encountered. "Central error checkers" are almost never robust enough for mission critical systems.
Go basically forces people to write high-quality, resilient code bases, and I like it for that.
[+] [-] humanrebar|8 years ago|reply
Actually C++ doesn't have immutable data unless you count compile-time constants and literals. You can declare things 'const', but that only provides a read-only (1) view on mutable data.
Also, to provide good 'const' support in containers, you usually have to provide extra read-only iterators that are largely identical to the writeable iterators. Go look at the C++ standard library. All the containers have both 'iterator' and 'const_iterator' (not just a 'const iterator').
Another approach is to just declare member variables 'const'. So you have a 'const map<int, int>' as a member variable. Except for that to work, you need to construct that member variable in the initializer list. The body of the constructor is too late. This likely means taking an already-initialized map as a constructor argument and copying it (probably exposing implementation details in the process) or writing a helper that returns the appropriate map type and copying it. Good optimizers will elide many or all of these copies, but relying on quality of optimizers isn't portable.
Anyway, I'd say the complexity of the Go approach isn't significantly worse that the C++ alternatives, if it's worse at all.
(1) Of course, with casting shenanigans or use of the 'mutable' keyword, anyone can circumvent 'const' without a lot of work.
[+] [-] searealist|8 years ago|reply
[+] [-] jpz|8 years ago|reply
How is this not immutable data, and "only provides a read-only view"?
It is linguistically guaranteed to be non-mutating - const-casts excepted of course.
All languages that are constructing const data types on the fly are putting them in a writeable pages in memory, after all.
[+] [-] Dr_Emann|8 years ago|reply
[+] [-] user5994461|8 years ago|reply
[+] [-] doublerebel|8 years ago|reply
Ended up with this. [0]
Turns out the Go authors don't think you should do this so I had to majorly alter and recompile the stdlib.
I still appreciate many go tools and the cross platform single binaries that work great on Solaris/Illumos. But in many cases the strong opinions of Go authors really stand in the way of getting work done.
[0]: https://github.com/doublerebel/publictransport
[+] [-] dvirsky|8 years ago|reply
[+] [-] ghthor|8 years ago|reply
[+] [-] lobster_johnson|8 years ago|reply
[+] [-] marcus_holmes|8 years ago|reply
I left it a while back, with a lot of the same frustrations. But then I tried a bunch of other things, and nothing seemed to work better. There seem to always be some frustrations with whatever language. And I found myself craving that simplicity again.
I might get annoyed with it again, and try some other stuff. And that might stick. But I suspect Go is the worst language except for all the others [1].
[1] http://www.goodreads.com/quotes/267224-democracy-is-the-wors...
[+] [-] andreasgonewild|8 years ago|reply
[+] [-] jpz|8 years ago|reply
In retrospect however, I was frustrated with these languages because I was wanting it to be something it wasn't. To my mind, the loss of that feature meant that programs were less expressive - particularly in large codebases - I had to read methods to work out if something was being modified or not.
To complain here about Go lacking immutability and constness features seems to be the same as complaining that it is missing in Python, Ruby or JavaScript. It's a fact that constness isn't a feature of these languages, if you want these features, use a different language.
[+] [-] blub|8 years ago|reply
Go should have had it from the beginning IMO, immutability can help avoid some coding mistakes and also makes it easier to reason about code. It'a nice match for a language with static typing.
[+] [-] fauigerzigerk|8 years ago|reply
It deprives the creators of that technology of the opportunity to react to any complaints and it deprives everyone else of the opportunity to choose a different technology from the start.
[+] [-] jbg_|8 years ago|reply
If that's enough for you to consider that a language provides a feature, just wrap the immutable ordered map implementation in your blog post in a library, publish it, and now Go provides that functionality too.
[+] [-] rvense|8 years ago|reply
[+] [-] Mikhus|8 years ago|reply
I don't know if there is a term for Go like programming. I'm tempted to call it minimalism. It may be uncomfortable for people coming from classical programming, but the simplicity of the language and the code is a clear win in the long run.
[+] [-] jondubois|8 years ago|reply
Now it seems everyone is bashing Go. Moral of the story; never buy into the hype whether it is positive or negative. I think that Go is just fine and so is Node.js.
Maybe the author of the article makes a valid point, but no language is perfect and ultimately you have to understand it well in order to use it well.
[+] [-] gipp|8 years ago|reply
[+] [-] japano1se|8 years ago|reply
It seems a lot of these articles, and programming language theory in general, is just complaining about features without any thought as to why they matter. What is this "problem" stopping him from doing? Why does it matter? And why should I abandon a language that has served me very well just because some edge-case feature is not supported?
[+] [-] dboreham|8 years ago|reply
Once I figure out what it is, I ask myself what problem it solves (the literature typically doesn't say). The answer tends to be one of:
1. Saves some typing.
2. Saves some work when refactoring.
3. Avoids some class of bug.
4. Highly useful in a kind of programming I don't do (e.g. compilers).
#1-2 in my experience are much more common than #3-4 and often #1-2 can be dealt with through tooling (IDEs for example).
[+] [-] unknown|8 years ago|reply
[deleted]
[+] [-] JamesMcMinn|8 years ago|reply
As noted in the post, generics could solve this, so I'm looking forward to Go 2 if indeed generics is included. There's a few QOL issues I'd like to see addressed in 2 which would make Go a lot nicer to work with, and I say that as someone who spends most of their time working on Go codebases.
[+] [-] noir-york|8 years ago|reply
I'm always wary of such "<language X> isn't great because..." posts as I find that the frustration is as much a function of the developer's competence, (lack of) imagination and familiarity with the language and its idioms, as it is with the language itself.
[+] [-] NateDad|8 years ago|reply
[+] [-] brango|8 years ago|reply
Might as well not pretend something is an ORM if it can't do the fundamentals (like upserts), fully hydrating structs, etc.
[+] [-] doublerebel|8 years ago|reply
So I wrote bellows [0], a helper to flatten and expand nested maps and structs. Not quite the same but might help someone.
[0]: https://github.com/doublerebel/bellows/blob/master/README.md
[+] [-] alkonaut|8 years ago|reply
[+] [-] pebers|8 years ago|reply
Java doesn't do that well at all though; its immutable maps can be used in place of mutable maps, since there's no way of having a Map interface that extends ImmutableMap to add extra operations, so you can get nasty runtime surprises if you've got the wrong one. Conversely, if you want a mutable map keyed on a non-trivial structure, you have to do just as much boilerplate as the author complains about to set up private members, setters, builders, etc.
Obviously C++ can do it quite readily, but nobody accused it of lacking features.
This is really just another use case for generics, and the discussion there is well-trodden. Go probably isn't the language for you if you value generics more highly than fast compile times, native binaries, etc, but I'm not sure why that still seems to come as a surprise.
[+] [-] _ph_|8 years ago|reply
I often read criticism of Go which is based on a missing "feature". In my experience, implementing these features is surprisingly easy in Go, as the building blocks are all there. In this respect, Go has some resemblance on Lego :).
My background contains a lot of Scheme/Lisp, so abstracting things with functions comes natural to me. It turns out that Go has basically the equivalent function concept (first class functions, closures, ..) so enables all the powerful abstractions coming from them.
[+] [-] PeterisP|8 years ago|reply
[+] [-] kuschku|8 years ago|reply
This is the opposite of DRY.
[+] [-] thinbeige|8 years ago|reply
Guess it is due to a different architecture and design which makes the client faster (e.g. chaindata is smaller) but does anyone know why they chose Rust instead of Go for the Parity client?
[+] [-] dvfjsdhgfv|8 years ago|reply
[+] [-] tonetheman|8 years ago|reply
Where is the list of problems that are easier to solve with generics?
[+] [-] rst|8 years ago|reply
[+] [-] justin66|8 years ago|reply
[+] [-] unknown|8 years ago|reply
[deleted]