I did some Go a year ago and liked it. Then coming back to it a year later after having done some functional programming in Clojure, it's not just the lack of generics that disrupt my flow but also having to think about all sorts of imperative programming details like naming and creating variables and scope placements in cases that would otherwise be unnecessary in a functional language.
I feel like I have been tainted by functional programming, and now Go feel like a joyless programming language which inevitably affect productivity.
I am looking for a replacement programming language to solve small problems for which Python/Ruby would have traditionally been used, good file system, stream and networking APIs, but concurrency as a first class citizen, garbage collected, fast startup time. Doesn't need to be good at long running programs, but it would be great if it can evolve to play a repeat role in a large system/process without the hassles of having to duplicate every written line of code into tests to prevent small changes from breaking things.
I am considering giving Haskell a try, but wondering if it might be overkill.
I think Haskell is a possible alternative to Go/Python/Ruby for these kinds of tasks.
But nothing's perfect. Some possible annoyances you might encounter:
* Much of the functionality required to be a valid "Python alternative" is not on the base packages. You'll have to install extra packages much sooner than with Python. And you'll have to know what packages to install.
* Despite having proper sum types, most I/O libraries in Haskell rely on exceptions for signalling errors. And Haskell has no proper stack traces. If you are not careful, you might find yourself pining for Go's multiple return values.
(Some would add "having to use the IO monad" to the list of annoyances, but I think it actually helps.)
Elixir sounds like it might be worth a look. Nice for scripting and as good as it gets for concurrent programming. Some of its design is inspired by clojure so you might feel right at home. http://elixir-lang.org
> I am looking for a replacement programming language to solve small problems for which Python/Ruby would have traditionally been used, good file system, stream and networking APIs, but concurrency as a first class citizen, garbage collected, fast startup time.
You just described Go, mostly. I can't think of a more fitting language given your stated requirements.
To your first point: I don't know what Go has to do with your preference of functional programming to imperative. Go doesn't claim to be a functional language so I just am not seeing the connection.
> I am looking for a replacement programming language to solve small problems for which Python/Ruby would have traditionally been used, good file system, stream and networking APIs, but concurrency as a first class citizen, garbage collected, fast startup time.
You may want to have a look at ClojureScript, which has come a long way in the last few years. While the tooling is still not as nice as Clojure-on-the-JVM in my opinion, it continues to get better everyday--this week support for a better REPL via NodeJS[1] was introduced, for instance. A lot of smart people are putting good effort into the compiler and ecosystem and I suspect we'll see a continued uptrend in cljs adoption. Anyway, it may be just what you want: an elegant, productive, pragmatic language with first-class support for concurrency (atoms, core.async) and well-suited to short-lived and scripting-domain problems. (Bonus you can write your frontend and backend in the same language if you want!)
If you don't need windows support, O'Caml with Jane Street's Core is pretty good. The Async module handles the concurrency bits, and the Pipe module handles the streaming parts.
You may try out using Scala as script language like this: "scala myScript.scala". The first startup times are slower, and even after that it may be slower than Python/Ruby but it still may be sufficient for your use case.
Using "sbt ~run" (re-runs your program automatically at every file change) may also be an option.
This question is not easy. Most languages I consider well designed do not become mainstream - which comes with a host of problems. Nowadays I think if having no type system at all is better than having a broken type system, thus I am considering picking up a mainstream dynamic language (JS being an obvious candidate - but the runtime puts me off).
Go has first-class functions, if it had generics it'd be trivial to put together libraries for most of the functional-collection goodies, while still having imperative style available for when you care about performance or when it's just simpler for the problem.
Scala is pretty nice. Concerning the JVM startup time: It's usually in the range of milliseconds for your use-case.
What gave the JVM a bad name regarding startup time are large JavaEE application servers which had dependencies to hundreds of MBs of libraries. In this case, startup is slow, otherwise: not so much.
Although i'm still very unsure about rob pike's argument that go don't need generics since it has interface, a recent experience :
After having implemented a mini web services in go and being fed up with its limited type system, i decided to stop coding in go and start recoding my project in java using what is often advertized here as the most minimal framework : dropwizard.
Well, i downloaded the framework, configured maven, started the hello world tutorial, and quickly found myself dealing with 2 xml files, 1 yaml file, multiple classe files ecerywhere, and couldn't get the whole stuff work immediately.
Then i realized that my golang implementation was already working, and that i completely understood my code and its potential performance characteristic, without having to have deep knowledge about the jvm internals, things like sevlet containers, or how the whole stack would deal with my global variables in a multi-threaded context. All my code was just one simple program.
My temporary conclusion right now is that golang type system is indeed extremely shite, but that if what you need is to develop simple yet efficient web services, it doesn't matter that much.
The article author points out, correctly, that Go code has far too much "interface{}" in it. That's an indication of real need for more power in the type system.
Generics may be overkill. Parameterized types may be enough. The difference is that you have to explicitly instantiate a parameterized type. Generic functions and classes get instantiated implicitly when used, which gets complicated. (See C++'s Boost.)
Go already has built-in parameterized types - "map" and "chan". Those are instantiated with "make", as with
p := make(map[string]int)
That's an instantiation of a parameterized type in Go. Go already has a full parameterized type mechanism. It's just that users can't define new parameterized types - all you get are "chan" and "map".
For parameterized types, struct definitions would need type parameters. A parameterized type would later be instantiated explicitly in a type declaration, with the parameters filled in and a new name given to the generated type. This is more explicit and less automatic than generics. There's no automatic specialization, as in C++.
This is enough that you can write, say, a binary tree library once and use it for multiple purposes. It's not enough to write C++'s Boost. That seems in keeping with the design of Go.
That's not really the full story though. The parameterized type isn't just `map[K]V`, it's more than that. The type `K` needs to be hashable, and not all types support that. e.g., slices do not: http://play.golang.org/p/IKp_I25NW2 It gets more complicated then that too, because composite types can be used for keys, but only if the type does not contain any non-hashable type.
Similarly, if you're going to build a binary tree data structure, then you probably need a way to compare elements. How do you express that constraint?
Standard ML has a similar limitation, which resulted in the presence of an `eqtype`, which is basically a way of saying, "any type T that can be compared for equality." But of course, ML has its modules...
So I'd argue that type parameterization alone doesn't really buy you much. You might say: well, we need bounded polymorphism then. OK. How do you define bounds? Maybe you figure out a way to abuse interfaces (but a bound isn't a type while an interface is a type), or you add a new way of specifying interfaces to the language.
The road to generics is long, complex and fraught with trade offs. Blessing a few key parametric types is a really interesting compromise, and I believe it's one that was absent in a similar practical manner in pre-generics Java and C++.
Excellent idea. I think maybe having strict type interfaces for the implementation so you could roll your own would be a good idea too; à la perl's tie HASH => TIEHASH, FETCH, STORE, et alia, TIEARRAY, FETCHSIZE, STORESIZE, et alia, etc. Obviously then you can have slices, arrays, maps (hashes, dicts), that can be use whatever backing store / behaviour you want / need. This will mean that behaviour of individual data collections may not be as clear cut in their behaviour (skeuomorphically) but the upside is more flexibility.
"Every computer language is an imperfect way of describing a potential solution
to a poorly understood problem" -- Me
I find it interesting when people wax poetic about how one language is better than another and how if we just did x, y or z then we'd have this perfect solution.
That said I appreciate the author's write-up of some challenges with Go. In the end the reality of any "product" is building what the user's need not what they ask for. That said I do worry that the Generics argument seems to be slowly approaching a religious war that will distract people from the other enjoyable aspects of Go. Worse yet it may calcify the Go development team in a way that will keep them from addressing the basic issues that generics might help solve. Either way Go is just another computer language and I'm happy to use it often to solve problems I'm working on. That said I use a lot of computer languages every week when I'm working on stuff, none of them are perfect and my solutions to the underlying problems are fragile and constantly evolving as the problems unfold. This is the nature of our business, to constantly do battle with poorly understood problems using imperfect tools in a world where we are fooled into thinking everything is black and white because at the core of our technology everything is a 0 or a 1.
"One Computer Language to bring them all and in the darkness bind them".. LOL
Languages may not be perfect, but language features can be compared, and some features are strictly more powerful than others.
There is scope for legitimate criticism of language design based on the expressive power^1 of their features.
In this case, generics would make Go strictly more expressive, as, without it you must write O(n) more code or perform a global refactoring to simulate it.
> That said I do worry that the Generics argument seems to be slowly approaching a religious war that will distract people from the other enjoyable aspects of Go
Because generics are important.We're not talking about crazy C++ templates here but a more rigid feature that would still make Go language more expressive.
Or why expose this interface {} feature and allow people to use it in a statically typed language?
People who chose Go obviously want type checking (along with CSP that makes go so awesome), or they would be using something else.
The big thing people miss is that while lack of generics might cost them effort worth X, other Go features benefits them Y. The value of Y is pretty large in case of Go, when it comes to concurrency and language level simplicity. It's hard to go wrong in Go.
Whoever designed Go seems to have some experience debugging and maintaining large concurrent systems.
When you design a language, you have to be so careful of things you add. Because otherwise you get C++ (or one of those cute let's-add-all-the-features-we-can-think-of scripting languages). In good and in bad. You can't remove features afterwards, so better not add a feature you can't get right from the beginning.
> This is the nature of our business, to constantly do battle with poorly understood problems using imperfect tools in a world where we are fooled into thinking everything is black and white because at the core of our technology everything is a 0 or a 1.
Isn't that extremely sad though? We finally have a perfectly precise tool, yet we keep building gooey piles of uncertainty on top of it.
Sure we're just imperfect humans, but that is what compilers and tooling are for - to point out our mistakes and help us get it right
I'm hoping that in the next 5-20 years Rust and Haskell (and later Idris) will change everything.
This article has some good points but I'm going straight for the first one: I don't miss generics.
Indeed, they are useful. I use them in Java every time I find a good reason to do it. So, I should say in another way: I don't miss generics in Go.
Given my experience [0] in Go in the last years, I realized that if you miss generics in Go, your code is trying to cope with too much.
I didn't miss them when I developed Mergo [1], a simple library to merge structs. I think we all agree if we should rewrite this in Java (or other language with similar type system) we would use generics at some point.
Did you miss it when you were working on larger codebases? The library you mentioned clocks at 500 LOC, and while the LOC metric is pretty inaccurate, it takes a lot more time to really miss language features.
The first point "no generics, no code reuse" is the best one. Go really needs generics/templates/macros or _something_. It doesn't even have subclassing, although you can kinda extend a type if you only use its public interface. I've been tempted to see if I could reasonably use the "text/template" package and feed that back into the Go compiler to achieve this.
The rest is mostly a Haskell fanboy whining that Go isn't Haskell.
Well, I want Go to have refcounting instead of current GC, so my programs could guaranty latency. Single-threaded runtime, without all the locks, slow channels, races and so on, because there is no point in so much overhead and complexity for the majority of programs. More consistency couldn't hurt, so I wouldn't have to assign anonymous function to a variable just to return it. Fast regular expressions compiled by the language compiler into a native code could improve matching, parsing and validating consistency, instead of writing lots of loops everywhere. Compiler warnings instead of errors on unused variables, imports, etc. to allow faster prototyping. Better testing culture with meaningful test names and line numbers with actual failed tests. Table testing suggested by Golang team doesn't even support that with the means of standard library, you have to use your own runtime.Caller(1) wrapper.
But generics? "Safer" type system? No, that's unnecessary complexity. As others pointed out, we already have Haskell and Rust for all those things.
Yeah, I almost never use map[string]interface{} when unmarshalling JSON. Most JSON has a static structure (and I'd argue that any JSON that doesn't is misbehaving). If the structure is static, you can simply use a struct that represents this[0].
If it doesn't, then you just use json.RawMessage to defer unmarshalling, but you can still dispatch to a finite number of structs that represent the universe of possible responses.
[0] And no need to write it all out by hand - if you have a single example JSON response, you can generate it automatically: https://github.com/ChimeraCoder/gojson
Every time I read an article criticizing Go I end up appreciating it even more.
Maybe it's because I've never felt the need to use generics and in all these articles the examples they give are functions of a few lines that would be quicker to write 2-3 times for different types than remembering generic syntax.
Maybe it's because they exalt one line functional functions over a nice, simple, easy to read FOR loop when the former are so difficult to read, figure out what they really do, what is the performance cost, debug...
Maybe it's because of the bogus examples they give like criticizing
> file, _ = os.Open("file.txt")
> file.Chmod(777)
for not handling explicitly a possible error when it's just because the example is ill written and the proper code is
> file, err = os.Open("file.txt")
> if err != nil {
> ... handle error ...
And here I stopped reading the article, it's always the same arguments over and over: more elegant and complex code vs. the un(cool) but oh my, so much simpler Go code.
What I find really amusing though it's how people are so smug in their writing, pointing out the "obvious errors" (billion dollar mistakes!) that the Go authors made and their "ignorance" of proven modern programming language constructs they could implement in Go.
There are two possibilities here, pick your preferred one.
1) Pike, Thompson & co. made obvious errors in designing Go because of their ignorance of programming languages and/or ineptitude
2) These bloggers claiming obvious errors in Go design don't really fully understand the trade-offs involved in what they ask for and ignore the fact that the Go authors have carefully thought about them and optimized accordingly
I will go back to programming in Go, I'd take writing for loops all the time vs. writing a one liner in Haskell and then agonize over using the lazy or strict version of it :)
One thing that is perhaps counter intuitive is how you do a lot of for loops compared to other languages (Compiled/Statically typed included). Once you let go your previous expectations, Go gets a lot nicer to use.
The amount of for loops you will write will also makes it obvious when you start doing O(n^2) operations in your methods. Same can be said with variables declaration and error verifications in Go. They are annoying at first, but then you understand by reading your code the places where error can occur or where you decided to ignore errors (By using _).
While the author has a lot of valid points, he forgets the goal of Go. Go was designed as a simple language, that is fast, but has similar power to existing dynamic languages such as python.
The tradeoff in these designs are to prevent tuple types, always keep using structs, prevent using algebraic types over structs. Allow for nil, but try prevent common Null errors etc.
Haskall is theoretical a much better language. But Golang was designed to be practical & simple over being mathematically sound, and theoretical better.
Author here. I think that is a very valid point. However, a lot of features I mentioned are present in other imperative languages, like Rust. The Go authors however picked other solutions I consider inferior, hence the article.
While I understand your comment, I have to disagree.
Programming languages should be kind of boring, and support the average developers' work. That is why Haskell will ultimate fail and fade away - most of the developers on this planet are simply not skilled enough to touch any of that stuff. Attempting to add all Ninja concepts to a language will just lead to a massive failure.
Go does a very good job on forcing even the most average developers to reach surprisingly succinct and elegant solutions. However, the lack of generics really forces in some situations to either use the reflection or stop being very DRY or elegant. Go wouldn't have to implement full generics to fix that. Just adding something similar to what interfaces are to methods for variables would do, without the rest of the moving parts.
"When the three of us [Thompson, Rob Pike, and Robert Griesemer] got started, it was pure research. The three of us got together and decided that we hated C++. [laughter] ... [Returning to Go,] we started off with the idea that all three of us had to be talked into every feature in the language, so there was no extraneous garbage put into the language for any reason."
The article mentions many uses of generics but it doesn't go over one of my favorites, which is that parametricity gives you "theorems for free".
For example, I recently wrote a compiler in Ocaml for a scripting language. The type I used for the syntax tree has lots of type parametsrs:
type ('id, 'str, 'enum) stmtf =
(* big ADT definition goes here... *)
The 'id type parameter is the type of identifiers (names), 'str is for strings (they get interpolated) and 'enum is for constants.
These type parameters give me some flexibility. When the parser builds the first syntax tree everything is still a string:
type syntaxtree = (string, string, string) stmtf
and after a pass to bind names the strings get converted to more informative types:
type boundtree = (nameId, interpolation, string) stmtf
Up to now, we could get this flexibility by being untyped and defining all these fields in stmtf as "void pointer" or "interface". But the generics lets us make everything type safe. Central to this is the "functor map" over stmtf:
It takes 3 functions telling what to do to the id, str and enum fields, respectively and converts the stmtf by applying those fucntions to each field. The neat thing is that in the implementation of map_stmtf if we forget to apply apply one of these callbacks to one of the fields the code would not typeckeck. The code also won't typecheck if we apply a callback to the wrong field - say apply the "id" callback to a "str" field. This lets us catch at compile time bugs that wouldn't even have been caught at runtime because in the initial syntaxtree both "id" and "str" fields are represented by "string" and are thus prone to being confused with one another. Both of these turned out to be pretty useful as I evolved my language and changed the structure of my syntax tree.
Liberal use of type parameters also gave many other benefits. For example it was trivial to modify the tree to add a line number next to all identifiers, all I had to do was update the type definition and then see the compilation errors to find the parts of my code that had to be updated.
type syntaxtree = ((position * string), string, string) stmtf
Finally, one extremely neat trick is that you can use type parameters in some places where you would have used recursive types. This pattern is described pretty well in this post I'm linking to and its the sort of thing that you can't really do without generics.
Ok, we all know the problem, but do you have a solution? Have you considered the researches done by others, for example this one: http://research.swtch.com/generic
[+] [-] tarikjn|11 years ago|reply
I did some Go a year ago and liked it. Then coming back to it a year later after having done some functional programming in Clojure, it's not just the lack of generics that disrupt my flow but also having to think about all sorts of imperative programming details like naming and creating variables and scope placements in cases that would otherwise be unnecessary in a functional language.
I feel like I have been tainted by functional programming, and now Go feel like a joyless programming language which inevitably affect productivity.
I am looking for a replacement programming language to solve small problems for which Python/Ruby would have traditionally been used, good file system, stream and networking APIs, but concurrency as a first class citizen, garbage collected, fast startup time. Doesn't need to be good at long running programs, but it would be great if it can evolve to play a repeat role in a large system/process without the hassles of having to duplicate every written line of code into tests to prevent small changes from breaking things.
I am considering giving Haskell a try, but wondering if it might be overkill.
Any suggestions?
[+] [-] danidiaz|11 years ago|reply
But nothing's perfect. Some possible annoyances you might encounter:
* Much of the functionality required to be a valid "Python alternative" is not on the base packages. You'll have to install extra packages much sooner than with Python. And you'll have to know what packages to install.
* Despite having proper sum types, most I/O libraries in Haskell rely on exceptions for signalling errors. And Haskell has no proper stack traces. If you are not careful, you might find yourself pining for Go's multiple return values.
(Some would add "having to use the IO monad" to the list of annoyances, but I think it actually helps.)
[+] [-] chrismccord|11 years ago|reply
[+] [-] wtf_is_up|11 years ago|reply
You just described Go, mostly. I can't think of a more fitting language given your stated requirements.
To your first point: I don't know what Go has to do with your preference of functional programming to imperative. Go doesn't claim to be a functional language so I just am not seeing the connection.
[+] [-] llambda|11 years ago|reply
You may want to have a look at ClojureScript, which has come a long way in the last few years. While the tooling is still not as nice as Clojure-on-the-JVM in my opinion, it continues to get better everyday--this week support for a better REPL via NodeJS[1] was introduced, for instance. A lot of smart people are putting good effort into the compiler and ecosystem and I suspect we'll see a continued uptrend in cljs adoption. Anyway, it may be just what you want: an elegant, productive, pragmatic language with first-class support for concurrency (atoms, core.async) and well-suited to short-lived and scripting-domain problems. (Bonus you can write your frontend and backend in the same language if you want!)
[1] http://swannodette.github.io/2014/12/29/nodejs-of-my-dreams/
[+] [-] Cyther606|11 years ago|reply
[+] [-] stonemetal|11 years ago|reply
[+] [-] rcarmo|11 years ago|reply
Other than that, I'm fiddling with LISP Flavored Erlang (lfe.io), but I've had previous Erlang exposure.
[+] [-] felipehummel|11 years ago|reply
Using "sbt ~run" (re-runs your program automatically at every file change) may also be an option.
[+] [-] unknown|11 years ago|reply
[deleted]
[+] [-] friendly_chap|11 years ago|reply
[+] [-] jbooth|11 years ago|reply
[+] [-] jramnani|11 years ago|reply
"Pixie is a lightweight lisp suitable for both general use as well as shell scripting."
https://github.com/pixie-lang/pixie
[+] [-] zodiakzz|11 years ago|reply
[+] [-] dominotw|11 years ago|reply
[+] [-] frowaway001|11 years ago|reply
What gave the JVM a bad name regarding startup time are large JavaEE application servers which had dependencies to hundreds of MBs of libraries. In this case, startup is slow, otherwise: not so much.
[+] [-] bsaul|11 years ago|reply
After having implemented a mini web services in go and being fed up with its limited type system, i decided to stop coding in go and start recoding my project in java using what is often advertized here as the most minimal framework : dropwizard.
Well, i downloaded the framework, configured maven, started the hello world tutorial, and quickly found myself dealing with 2 xml files, 1 yaml file, multiple classe files ecerywhere, and couldn't get the whole stuff work immediately.
Then i realized that my golang implementation was already working, and that i completely understood my code and its potential performance characteristic, without having to have deep knowledge about the jvm internals, things like sevlet containers, or how the whole stack would deal with my global variables in a multi-threaded context. All my code was just one simple program.
My temporary conclusion right now is that golang type system is indeed extremely shite, but that if what you need is to develop simple yet efficient web services, it doesn't matter that much.
[+] [-] Animats|11 years ago|reply
Generics may be overkill. Parameterized types may be enough. The difference is that you have to explicitly instantiate a parameterized type. Generic functions and classes get instantiated implicitly when used, which gets complicated. (See C++'s Boost.)
Go already has built-in parameterized types - "map" and "chan". Those are instantiated with "make", as with
That's an instantiation of a parameterized type in Go. Go already has a full parameterized type mechanism. It's just that users can't define new parameterized types - all you get are "chan" and "map".For parameterized types, struct definitions would need type parameters. A parameterized type would later be instantiated explicitly in a type declaration, with the parameters filled in and a new name given to the generated type. This is more explicit and less automatic than generics. There's no automatic specialization, as in C++.
This is enough that you can write, say, a binary tree library once and use it for multiple purposes. It's not enough to write C++'s Boost. That seems in keeping with the design of Go.
[+] [-] burntsushi|11 years ago|reply
Similarly, if you're going to build a binary tree data structure, then you probably need a way to compare elements. How do you express that constraint?
Standard ML has a similar limitation, which resulted in the presence of an `eqtype`, which is basically a way of saying, "any type T that can be compared for equality." But of course, ML has its modules...
So I'd argue that type parameterization alone doesn't really buy you much. You might say: well, we need bounded polymorphism then. OK. How do you define bounds? Maybe you figure out a way to abuse interfaces (but a bound isn't a type while an interface is a type), or you add a new way of specifying interfaces to the language.
The road to generics is long, complex and fraught with trade offs. Blessing a few key parametric types is a really interesting compromise, and I believe it's one that was absent in a similar practical manner in pre-generics Java and C++.
[+] [-] tankenmate|11 years ago|reply
[+] [-] kator|11 years ago|reply
I find it interesting when people wax poetic about how one language is better than another and how if we just did x, y or z then we'd have this perfect solution.
That said I appreciate the author's write-up of some challenges with Go. In the end the reality of any "product" is building what the user's need not what they ask for. That said I do worry that the Generics argument seems to be slowly approaching a religious war that will distract people from the other enjoyable aspects of Go. Worse yet it may calcify the Go development team in a way that will keep them from addressing the basic issues that generics might help solve. Either way Go is just another computer language and I'm happy to use it often to solve problems I'm working on. That said I use a lot of computer languages every week when I'm working on stuff, none of them are perfect and my solutions to the underlying problems are fragile and constantly evolving as the problems unfold. This is the nature of our business, to constantly do battle with poorly understood problems using imperfect tools in a world where we are fooled into thinking everything is black and white because at the core of our technology everything is a 0 or a 1.
"One Computer Language to bring them all and in the darkness bind them".. LOL
[+] [-] dons|11 years ago|reply
There is scope for legitimate criticism of language design based on the expressive power^1 of their features.
In this case, generics would make Go strictly more expressive, as, without it you must write O(n) more code or perform a global refactoring to simulate it.
1. http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.51.4...
[+] [-] aikah|11 years ago|reply
Because generics are important.We're not talking about crazy C++ templates here but a more rigid feature that would still make Go language more expressive.
Or why expose this interface {} feature and allow people to use it in a statically typed language?
People who chose Go obviously want type checking (along with CSP that makes go so awesome), or they would be using something else.
[+] [-] vardump|11 years ago|reply
Whoever designed Go seems to have some experience debugging and maintaining large concurrent systems.
When you design a language, you have to be so careful of things you add. Because otherwise you get C++ (or one of those cute let's-add-all-the-features-we-can-think-of scripting languages). In good and in bad. You can't remove features afterwards, so better not add a feature you can't get right from the beginning.
[+] [-] spion|11 years ago|reply
Isn't that extremely sad though? We finally have a perfectly precise tool, yet we keep building gooey piles of uncertainty on top of it.
Sure we're just imperfect humans, but that is what compilers and tooling are for - to point out our mistakes and help us get it right
I'm hoping that in the next 5-20 years Rust and Haskell (and later Idris) will change everything.
[+] [-] nine_k|11 years ago|reply
I think this is a mistake. A lot of business is run this way, though.
[+] [-] im_dario|11 years ago|reply
Indeed, they are useful. I use them in Java every time I find a good reason to do it. So, I should say in another way: I don't miss generics in Go.
Given my experience [0] in Go in the last years, I realized that if you miss generics in Go, your code is trying to cope with too much.
I didn't miss them when I developed Mergo [1], a simple library to merge structs. I think we all agree if we should rewrite this in Java (or other language with similar type system) we would use generics at some point.
[0] https://github.com/imdario?tab=repositories
[1] https://github.com/imdario/mergo
[+] [-] friendly_chap|11 years ago|reply
[+] [-] colin_jack|11 years ago|reply
Was wondering what you mean by that (not disagreeing just interested)?
[+] [-] brianolson|11 years ago|reply
The rest is mostly a Haskell fanboy whining that Go isn't Haskell.
[+] [-] zzzcpan|11 years ago|reply
But generics? "Safer" type system? No, that's unnecessary complexity. As others pointed out, we already have Haskell and Rust for all those things.
[+] [-] jmnicolas|11 years ago|reply
[+] [-] nhaehnle|11 years ago|reply
There is nothing exponential about nub. Given only equality comparisons, detecting and eliminating duplicate elements requires quadratic running time.
[+] [-] friendly_chap|11 years ago|reply
[+] [-] zachgersh|11 years ago|reply
type JSON map[string]interface{}
Go ahead and use the json.RawMessage type instead:
http://golang.org/pkg/encoding/json/#RawMessage
Not even going to wade into the lack of generics war, still very happy without them and going on a full year of Go usage.
[+] [-] chimeracoder|11 years ago|reply
If it doesn't, then you just use json.RawMessage to defer unmarshalling, but you can still dispatch to a finite number of structs that represent the universe of possible responses.
[0] And no need to write it all out by hand - if you have a single example JSON response, you can generate it automatically: https://github.com/ChimeraCoder/gojson
[+] [-] gws|11 years ago|reply
Maybe it's because I've never felt the need to use generics and in all these articles the examples they give are functions of a few lines that would be quicker to write 2-3 times for different types than remembering generic syntax.
Maybe it's because they exalt one line functional functions over a nice, simple, easy to read FOR loop when the former are so difficult to read, figure out what they really do, what is the performance cost, debug...
Maybe it's because of the bogus examples they give like criticizing
> file, _ = os.Open("file.txt")
> file.Chmod(777)
for not handling explicitly a possible error when it's just because the example is ill written and the proper code is
> file, err = os.Open("file.txt")
> if err != nil {
> ... handle error ...
And here I stopped reading the article, it's always the same arguments over and over: more elegant and complex code vs. the un(cool) but oh my, so much simpler Go code.
What I find really amusing though it's how people are so smug in their writing, pointing out the "obvious errors" (billion dollar mistakes!) that the Go authors made and their "ignorance" of proven modern programming language constructs they could implement in Go.
There are two possibilities here, pick your preferred one.
1) Pike, Thompson & co. made obvious errors in designing Go because of their ignorance of programming languages and/or ineptitude
2) These bloggers claiming obvious errors in Go design don't really fully understand the trade-offs involved in what they ask for and ignore the fact that the Go authors have carefully thought about them and optimized accordingly
I will go back to programming in Go, I'd take writing for loops all the time vs. writing a one liner in Haskell and then agonize over using the lazy or strict version of it :)
[+] [-] pothibo|11 years ago|reply
The amount of for loops you will write will also makes it obvious when you start doing O(n^2) operations in your methods. Same can be said with variables declaration and error verifications in Go. They are annoying at first, but then you understand by reading your code the places where error can occur or where you decided to ignore errors (By using _).
It's a different approach and it's refreshing.
[+] [-] TeeWEE|11 years ago|reply
The tradeoff in these designs are to prevent tuple types, always keep using structs, prevent using algebraic types over structs. Allow for nil, but try prevent common Null errors etc.
Haskall is theoretical a much better language. But Golang was designed to be practical & simple over being mathematically sound, and theoretical better.
[+] [-] NateDad|11 years ago|reply
[+] [-] friendly_chap|11 years ago|reply
[+] [-] erglkjahlkh|11 years ago|reply
Programming languages should be kind of boring, and support the average developers' work. That is why Haskell will ultimate fail and fade away - most of the developers on this planet are simply not skilled enough to touch any of that stuff. Attempting to add all Ninja concepts to a language will just lead to a massive failure.
Go does a very good job on forcing even the most average developers to reach surprisingly succinct and elegant solutions. However, the lack of generics really forces in some situations to either use the reflection or stop being very DRY or elegant. Go wouldn't have to implement full generics to fix that. Just adding something similar to what interfaces are to methods for variables would do, without the rest of the moving parts.
[+] [-] Sphax|11 years ago|reply
[+] [-] braceta|11 years ago|reply
[+] [-] likeclockwork|11 years ago|reply
[+] [-] lmm|11 years ago|reply
[+] [-] ufo|11 years ago|reply
For example, I recently wrote a compiler in Ocaml for a scripting language. The type I used for the syntax tree has lots of type parametsrs:
The 'id type parameter is the type of identifiers (names), 'str is for strings (they get interpolated) and 'enum is for constants.These type parameters give me some flexibility. When the parser builds the first syntax tree everything is still a string:
and after a pass to bind names the strings get converted to more informative types: Up to now, we could get this flexibility by being untyped and defining all these fields in stmtf as "void pointer" or "interface". But the generics lets us make everything type safe. Central to this is the "functor map" over stmtf: It takes 3 functions telling what to do to the id, str and enum fields, respectively and converts the stmtf by applying those fucntions to each field. The neat thing is that in the implementation of map_stmtf if we forget to apply apply one of these callbacks to one of the fields the code would not typeckeck. The code also won't typecheck if we apply a callback to the wrong field - say apply the "id" callback to a "str" field. This lets us catch at compile time bugs that wouldn't even have been caught at runtime because in the initial syntaxtree both "id" and "str" fields are represented by "string" and are thus prone to being confused with one another. Both of these turned out to be pretty useful as I evolved my language and changed the structure of my syntax tree.Liberal use of type parameters also gave many other benefits. For example it was trivial to modify the tree to add a line number next to all identifiers, all I had to do was update the type definition and then see the compilation errors to find the parts of my code that had to be updated.
Finally, one extremely neat trick is that you can use type parameters in some places where you would have used recursive types. This pattern is described pretty well in this post I'm linking to and its the sort of thing that you can't really do without generics.http://lambda-the-ultimate.org/node/4170#comment-63836
[+] [-] jackielii|11 years ago|reply
[+] [-] sacado2|11 years ago|reply
[+] [-] fishnchips|11 years ago|reply