I have decided to get proficient in Go about 3 years ago and I am glad I did. One of the discoveries in the process was that Go enables you to do things that previously were extremely difficult and you'd typically not even consider as options.
Many of the "old" ways of thinking do not apply to Go, and I had to get over the phase of looking for an ORM or webapp or testing frameworks - they don't exist because they are not necessary, and those that do aren't any good really.
I ended up inventing my own challenge - a time-series database backed by PostgreSQL (http://github.com/tgres/tgres), because I needed a difficult problem. Tgres is still not fully baked, but what it does couldn't possibly be done in e.g. Python, and not in C in as little time (even though it's been two years in development).
Contrary to what the blogs say, Go is _not_ an easy language to learn. Interfaces, goroutines, channels, selects, etc. will take a long while to become second nature, and if you're not using them then you're not really getting anything out of Go.
With respect to all the criticism Go is getting out there, e.g. lack of generics - I honestly can't say there is anything that bugs me at all. Some things may take getting used to, but usually in the end you're glad to have done so.
As someone who has done a lot of programming in my life (in Python, C (remember mod_python?) and also Ruby, and (with displeasure) Java) I can say that Go is a big deal in the language landscape and will not long from now be as big as Python - in fact I think it is Python developers who are most likely to switch over to Go for their next project.
> Many of the "old" ways of thinking do not apply to Go
Whatever. Just because the language is effectively broken doesn't make practices in other languages irrelevant. And it's funny when you talk about the "old" ways of thinking in language that basically promotes C styles errors and bad macros through go gen.
When I say the language is broken, look at Go type system, then look at how Go reflection makes the language look a dynamically typed one. It would have been smarter to tone down the reflection and make it unnecessary with a smarter type system. If it is there then it is because it was necessary for Go designers. If it had not been, then Go would not need that insane reflection.
Now Go back to your Go code and remember all tricks you need to learn when working with arrays :
While I'd like to have generics in go, one of the upsides of the lack of generics is that your code becomes less idiosyncratic and more idiomatic. Too often in C++ for example, I find myself frustrated reading code where templates are unintentionally over-used because the programmer needlessly introduced an abstraction where the concrete would have sufficed. There is a certain pleasure in moving from the concrete to the abstract and finding the inner connection between things, but this can also get in the way or distract from the immediate problem.
The same issue comes up in Common Lisp where you find a codebase with heavy use of macros which effectively build a DSL within the program. Working alone, this can be fun, and can produce elegant code, but it means anyone new to a project needs to first familiarize themselves with the world you've built.
When many people work on a project it helps if everyone speaks the Queen's English, as it were.
> While I'd like to have generics in go, one of the upsides of the lack of generics is that your code becomes less idiosyncratic and more idiomatic.
That sounds like a deepity[1] if ever I've heard one.
Just to add something a little more substantive: It may not be obvious at first sight (esp. in e.g. C++ or Java), but 'generics'/'parametric polymorphism' can actually incredibly powerful at restricting what your program can do and thus actually restrict the set of valid programs which will still satisfy the type checker. This is used to great effect in e.g. ML or Haskell and can be used somewhat effectively in Java/Scala code with subtype bounds. Of course in Haskell, being a language from the 1990s[2], you can use this to effectively restrict the types of effects used by any given function (database I/O, arbitrary I/O, networking, etc.).
[2] Ok, this is slightly exaggerated since monads-for-IO were discovered quite a bit after 1990, but still... parametric polymorphism is crucial for this use case.
> Your code becomes less peculiar to you and more in line with the peculiarities of the language as it's used by most people. Shallow enough? Don't let an allergy to postmodernism become an allergy to a turned phrase.
I'm objecting to the attempt at sloganization as if witty/clever phrases mean anything. FTR, I also think this is a bad thing in FP. My "favorite" example being "if it compiles, it works" (or similar).
> Not sure what the rest of your comment has to do with my point, which was about, if you like, the pragmatics of programming.
My point, though perhaps somewhat obscure, was that eliminating a lot (i.e. "classes") of bugs up front (e.g. by exploiting parametric polymorphism) means that you spend less time fixing bugs... which means that you spend more time getting things done.
(Now, the jury is still out on whether static type checking leads to more productivity than dynamic type checking, but given that you're interested in Go, I'm guessing you're probably leaning towards 'static' anyway.)
> Not concerned with the space of programs that type check. Insufferable haskellers...
I used Haskell (and ML, you'll notice) as an example, but if you're not concerned with the space of programs that type check -- why are you even using a statically checked language? Surely you'd be infinitely more productive in a dynamically checked language? I'm saying you should be concerned and demand concern from the designers of your language.
In my experience Haskell-people are not insuffereable, but have a history of a rather academic humility in the same manner that, historically, the ruby community used to.
Perhaps this is changing, but my guess is the prior comment was by a person who appeals to Haskell more often than uses it.
> my guess is the prior comment was by a person who appeals to Haskell more often than uses it.
Two points:
One, I'm not keeping an exact score of how often I mention Haskell vs. use it, but I can assure you that I use Haskell almost every single day.
Two, this constant anti-academia thing is really getting tiresome at this point... but just for the record: I've never worked in academia and didn't even encounter Haskell until I was out of university. My interest and praise for Haskell (and Ocaml) is entirely based on practical experience.
Since when is powerful reflection a drawback or a sign of a broken language? In my experience reflection is not used all that often in Go and it's nowhere near as powerful as say .NET or JVM reflection - which means package private stuff stays package private, unlike the JVM where the JIT must assume that any type can change at any time.
Go has its quirks, and many design decisions that I find dubious, but so does every language I've used. I'd still much rather tackle a new project in Go than Python or C++ or Java. It's efficient, it's got great tools and a fully-stocked standard library, a fast compiler, static type checking, type inference, and is extremely easy to read other's code because the language is so simple. There aren't a million ways to write cryptic code, unlike say Scala - which appeals to me more on paper but is a disaster to work with in comparison.
> Since when is powerful reflection a drawback or a sign of a broken language?
When it is not balanced by an expressive type system, it becomes a cop-out where anything goes,including creating adhoc types at runtime ( reflect.StructOf() , reflect.SliceOf() , ... ), because it's the only way to get a little bit of expressiveness of out that language.
Your position is that statically typed languages shouldn't have reflection? I think that's a pretty fringe position to take that not many programmers would agree on. The JVM and .NET static typed languages have reflection, C++ will (hopefully) get reflection one of these days soon as it's been talked about for a while. Even rust has reflection. I think statically typed languages without reflection are the exception rather than the norm.
Whatever. Just because the language is effectively broken doesn't make practices in other languages irrelevant.
Classic. All languages stink, somewhere. The classic programmer Dunning-Kruger mistake: evaluating one language without self-awareness of all of the (probably unconscious) cost/benefit optimizations that mostly apply to the other language. This is like Smalltalkers and C++ programmers in the 90's arguing about the design decisions that went into Java.
Your definition of "effectively broken" actually means "doesn't fit my preferences." I'd expect "effectively broken" to more usefully mean "basically, very few people want to use it in production." Smalltalk now fits that. (Mostly through bad business/marketing decisions and incompatibility with mainstream programming expectations.) I bet there's many, many languages you might even call "elegant" that fit that as well.
basically promotes C styles errors and bad macros through go gen.
Funny, but my experience is that Golang basically eliminates my dealing with those. I'm writing an MMO, and my debugging time in that category amounts to literally 2 minutes. (Basically, oh duh, that member function should take a pointer reference. Otherwise, it can't mutate state!)
When I say the language is broken, look at Go type system, then look at how Go reflection makes the language look a dynamically typed one.
If you're such a poor programmer, that you can't get about the same level of type safety around array code in a Golang environment, as say, in a C++ environment, then just find a different language. On the other hand, if you can show an increase in productivity doing it your way in some kind of A/B quantitative comparison on actual production level code, then go ahead and take that show on the road. Go to conferences, write papers, and get yourself a nice consulting business preaching your gospel, getting paid, and advancing the almost-a-field of programming. Otherwise, just stop making noise and trolling.
To be fair, there are some languages with pathologies that are publicly apparent, like PHP with webapp security and C with security exploits. There are problems with programming in the large with C++ -- some involving the very features you probably think are admirable. If you have experience with Golang in production, and you've tried mitigating the type-system weaknesses to get compile-time or test-time warnings out of the tooling, and this incurred your company some cost, then talk about that. Otherwise, you are just making worthless angry programmer noise devoid of grounding in actual practice.
What a warm an welcoming community the Go community is /s
C++ doesn't have reflection, because it doesn't need it. But somehow Go needs it ? Reflection is a cop-out, especially in a language that has a simplistic type system. And don't get me started on struct tags. If you cant see the obvious issue with all these "features", in comparison of the lack of expressiveness of types AT COMPILE TIME then good for you.
Gotta tell ya, criticizing Go for lacking generics, which Go supporters argue are unnecessary, while simultaneously arguing that C++ doesn't have reflection because it "doesn't need it" is ironic in the extreme.
Very few Go supporters would say that generics are unnecessary, especially not the development team. But they are not required at any price and so far no good solution for implementing them has been found. So they are delayed until that day.
While I miss them certainly, I strongly prefer them being absent to a bad and complex/confusing implementation.
But the problems go further. The go type system is littered with exceptions. Why ? Because append, make, maps, channels in for loops, channels in while loops, ... all have their own specific entries in the type system.
Go has generics ... but only for the core team. Go has polymorphism (there are polymorphic functions, all of which BEHAVE differently in the type system), there are return type polymorphic functions (functions that behave differently depending on whether you assign their result to one or two variables).
One look at github will tell you exactly what the go way of handling errors does in practice : ignoring errors. Golang code is like C code: it makes it sooo easy to just ignore errors. Second thing: errors are so hard to localize, as they're not tagged with location or stack trace. So I often find myself looking for string errors, while in Java/C# in 99% of cases I know exactly where shit happened. In Go I know in 20% of cases or so.
Speed. Go manages to be slower than Java, except in startup. For a compiled language, that's an accomplishment.
Besides the reactions to Go seem rather typical: it's simple and very, very limiting. Operations people are beside themselves. Predictable (although the ignoring of errors, channels and difficult to localize errors plus a number of mistakes in the standard library detract from that more than a little bit). Programmers don't like it.
I found the https://github.com/juju/errors library to be a very practical way to include tracing info in the error values.
I wish the core error library behaved like that, and that the core language made it less verbose.
This is a typical thing in Go: the language is kept stable and backward compatibility is king. Solutions for some things are left to the community.
Other languages changed a lot over the years, getting better but frustrating people because of breaking changes. I believe this promise of stability paved the way for much of Go's success, despite many shortcomings
> This is a typical thing in Go: the language is kept stable and backward compatibility is king. Solutions for some things are left to the community.
C++16 beta compilers can compile the very first C program just fine. How's that for backwards compatibility ? 44 years. In fact, this is exactly what's frequently blamed for C++'s complexity. Support lots of weird edge cases because "at some point it worked". Over 44 years (wtf C is old now).
Java - same. ML - same. C++ - same.
> I believe this promise of stability paved the way for much of Go's success, despite many shortcomings
Actually for most of it's 7 year life the exact opposite is true. Go regularly broke it's weird cases (I would say because getting a compiler without a sound type system working well is a road that is littered with mines). This is why "gofix" exists ( https://blog.golang.org/introducing-gofix ). Go's authors are crediting this with Go's "simpleness".
As I said, generics are missing, and the core team is working at them. But I really appreciate that they do not implement something like the Java generics.
If your code produces errors, you can of course add location information like stack traces, there is an API for that.
I have not benchmarked Go against Java explicitly, but I do benchmark against C and it compares very nicely in performance to C code, so for most tasks it should be as fast or faster than Java code. Go gives you more flexibility about memory layout (true value types), which keeps GC times down.
I am a programmer, with 20 years of Java on my belt and I quite love Go.
> I have not benchmarked Go against Java explicitly, but I do benchmark against C and it compares very nicely in performance to C code,
On average, Go code is about 50% as fast as C code in calculations and algorithms, usually, and has more lines of code for equivalent functionality without cheating. Do your benchmarks tell you something different ?
Java is about 10% slower than C. 30% or so if you "don't cheat" (meaning write a direct translation of C code into java, e.g. using char[] instead of string)
There's also that C encourages you to write faster code, at least it does for me. Java, Go, and every other language I know, encourage me to do string mangling by making lots of temporary strings. C doesn't really allow for that, instead making me think about doing in-place string editing, which is far, far faster than using copies. Of course, get the tiniest little detail wrong and it'll blow up, but there's no beating it in speed.
Yes, in my daily experience, Go is roughly on par with C code. There are a few constructs that the Go compiler does not optimize yet (jump tables), but other than that they are pretty close.
What a warm an welcoming community the Go community is /s
I have to say, I've had the "warm an welcoming /s" from the Go community myself. I'll give you a hint: You can also get the same from Smalltalk if you complain that a method can't have more than 256 temporary variables, and that this means Smalltalk isn't a real language. (How that demonstrates cluelessness is left as an exercise.) Annoying Dunning-Kruger crap that would only be clever and well informed if posted as a deliberate troll -- I leave that to you for what sort of welcome one should expect.
I'll also note that you are "copping out" by responding to exactly zero of my arguments and deflecting attention instead. Probably because you can't.
Reflection is a cop-out, especially in a language that has a simplistic type system.
Says who? This could just as well apply to Smalltalk. If you're pedantic about it, you could even say Smalltalk-80 is a strongly typed compiled language. Reflection is just another tool that has trade-offs and potential problems like any number of programming language tools. And if you said it was a language design "smell" you'd be right. But to a dedicated practitioner, no programming language is free of those.
Type information isn't reflection. In C++, given a pointer, you can only discover that it points to a Foo if you know that Foos exist.
If you don't, you'll never be able to find the name of the type, whether it has a member named "Bar", what the type of that member is, whether it can be set, whether Foo has a method named "Baz", what its argument types are, etc.
Even if you do know Foo is a type, the programmer has to do most of that stuff. For example, one cannot write a function that prints all fields of an object passed to it without specifying its concrete type. Add a field, and you have to adjust that function.
The only thing you can reliably do is determine whether Foo implements a given interface (= abstract base class) but again, that's only if you know that interface exists. You cannot determine the name of a superclass or the depth of an object's class hierarchy, for instance.
Can you create adhoc types from nothing with RTTI ? because in Go you can, at runtime. Is it what a statically typed language which community claims generics are unnecessary should prioritize as a feature ? runtime magics ? really ?
So your argument goes no deeper than, "(Say stuff that might sound bad on the surface) Really?" Can you be more substantive than appeals to prejudice? (For a change)
All languages might stink "somewhere", but there are absolutely better and worse languages, and it's not just a matter of different tradeoffs.
You can give 10 language writers a set of the same tradeoffs to strive for, even hand them in order: it doesn't mean that the 10 resulting languages will be of the same quality.
I use a testing framework[0] all the time in Go, and while I appreciate it's possible to write tests without one I think it's just pig-headed to not have something like that in the standard library.
If you just use testing.T you will most likely end up with a lousy, developer-hostile test suite. (Also, the default coverage metrics are very very weak. But I love Go just the same.)
I disagree that you will end up with a `lousy, developer-hostile test suite`. All testify does, to my experience, is change three line if-statement to a one line assert/require that ignores type safety. Aside from that, it is up to the developer to still write good cases, adopt table driven tests when they make sense, and provide enough feedback in test output to debug a broken test.
Testify makes a moderate improvement to writing tests, but I still love it. Multiply the improvements across a hundred tests and the difference is huge.
I wouldn't even call testify a framework. You can just as easily mix tests that don't use testify with tests that do use testify. It's more of a toolkit with helper functions to help you write test, which you can pick and choose as needed. But I agree, I've done the plain vanilla Go testing in the past, and testify has drastically cleaned up my tests.
Strong, strong disagree on the idea that ORMs aren't necessary because of some property of Golang itself. Lack of decent ORMs is probably my biggest complaint about Go, and I think if you look at a lot of large web app packages you'll find people are all writing their own 40-60% of an ORM.
When I started using go (coming from rails), ORM was really the thing I missed the most. And this made me realize I actually didn't know much about postgres.
I started using pg functions, views, triggers, etc and found it extremely convenient. Now, my sql strings in go are mostly `SELECT * FROM func_name(<$params>)` and I'm happy with that.
I'm not saying ORMs are useless (active_record is an incredibly cool tool), but it's been surprisingly way easier to live without them that I'd thought.
Even writing Postgres functions, you're still writing the same basic SQL statements (for the 80% of your database interactions that are essentially CRUD) over and over again.
People used to defend writing things in assembly language for similar reasons, as a way of getting more machine sympathy. I'm not buying it.
I'll argue this point. It takes me 30 seconds to write a SQL query. It's not hard. On the flip side, I don't end up running queries that haul megabytes of data in just to peek at one column.
SQL is already much higher level than assembly. Piling on more abstraction just increases the likelihood that the code is doing extraneous work.
I agree SQL is not hard, but getting the data into objects is definitely repetitive work. ORMs are a double-edged sword where people who don't know SQL write bad ORM code because they can, but a good ORM gives various ORM level that allow one to do the right thing and still save a lot of repetitive boilerplate.
If you avoid an ORM, the typical medium sized CRUD app usually ends up with some poorly conceived bespoke abstractions that would be better used by some flavor of ORM. I'm with you that ORMs make it way too easy for junior devs to blow their leg off, but I still think they provide some very solid abstractions when used correctly.
Fully agree. ORMs save you typing if you just need to wrap a table, but for complex multi-table queries I have seen really bad performance as a result. I rather prefer to capture my queries in a view, and then wrapping that is so trivial, that I don't need an ORM.
> People used to defend writing things in assembly language for similar reasons, as a way of getting more machine sympathy. I'm not buying it.
I have to assume those people did not first learn and use C, then went to assembly, but rather the opposite ;) So, the comparaison does not apply, here.
> you're still writing the same basic SQL statements (for the 80% of your database interactions that are essentially CRUD)
It's not the case, actually. My functions are way more expressive that the crud I did with orms. With PL/pgSQL, I group several queries in a same pg function, I always do more that a simple insert or update. I also fine tune my returned fields - which most of the time are composite from several tables / computations.
This is really outsourcing all data manipulation to the database, the comparison that applies is more with a presenter/serializer than with an orm model.
What are the typical trade-offs of outsourcing all the data manipulation & lookups to the DB. Are there cases when this is absolutely what you don't want to do?
I realize this is a very general and vague question but it's something I've been pondering about.
Initially, I would use `\ef <func_name>` in psql and just write and try my function, but this turned out to be not as pleasant as writing code in an editor in a dedicated file. The annoying thing with pg functions is that, if you want to replace them, you have to provide exactly the same in and out parameters. This means that if you want to change the parameters, you have to drop the function and create a new one.
This has been annoying at first, but now I'm more comfortable with it. I write my functions as migration files [1] and just migrate/rollback to do my tests. I guess I started to give more attention to the input and output parameters from the start in the mean time.
The cases where I won't use functions is when I want to do something extremely simple, like `SELECT email FROM users WHERE id = 1`. It's just not worth writing a function for that. It doesn't happen often that this is all I need to process my request either.
We prevented the grossness of writing things over and over again by just using Golang's script generation and some other stuff. http://github.com/spacemonkeygo/dbx.
That's why we created https://github.com/go-reform/reform. It gives you type-safe (without interface{}) helpers for both common cases and reading custom query results.
One of my biggest complaints about ORMs is that I often know the exact query I need to write, and it is a pain to compose it with the ORM. Simple CRUD, sure, maybe. Beyond that though... Arguably one of the biggest benefits of an ORM is sanitization. You get that for free with Go. I've done quite a bit of work with DBs and Go, and I've not seen the need for an ORM, just some library helpers.
You get that for free in any language that allows you to pass parameters to a query. There is nothing Go specific.
> One of my biggest complaints about ORMs is that I often know the exact query I need to write, and it is a pain to compose it with the ORM.
I agree that ORMs are often their own DSL one has to learn, when SQL is already there. In that sense they are opaque. Yet ORM still deal with relationships that have to be manually wired with queries, since AFAIK SQL query are still 2 dimensional. In my opinion query builders are often a nice trade-off between a fully featured ORM and raw SQL : you get SQL abstraction thus query reuse at minimal cost without the complexity.
An other alternative is Jooq style code generation/scaffolding but each time a tool like that is added to a compilation pipeline it is yet another dependency to manage. Code generation is opaque in its own ways.
Finally there is the nuclear option in shape of HQL but it sounds like a half baked farce to go back to "SQL" to avoid SQL.
I myself chose to go with query builders, in fact i'm porting Doctrine DBAL to Go as we speak.
I have written a small webapp in Go while learning it and I didn't use an ORM, but the feedback I got from reddit is that at some point in time I'd need to use an ORM. What are your thoughts on the usage of ORM? +ve points vs -ve points?
> In contrast, wrapper functions are low cost, easy to write, and recognizable because you write them yourself.
Isn't saying that you should use functions that are "recognizable because you write them yourself" basically saying "you shouldn't use libraries?" This seems like the obvious NIH fallacy.
Not necessarily. It could also mean that these functions are more specific, have names that make sense in the concrete context and don't have any unnecessary parameters you need to think about. When it comes to ORMs it's even worse. They are often not libraries but frameworks with tons of non-local settings and state you need to know about (or else...).
Not invented here also means not invented for the specific task at hand but for a much more general set of use cases. In my view it's a trade-off, not a fallacy.
In my experience there is one property of Go that minimizes the need for a good ORM, and that is that the problem domain Go excels in does not have great overlap with the problem domain that ORMs excel in. What interfacing with a relational database does come up is of the nature that does not fit well into the ORM model anyway.
Of course, you can write software in the same domain where ORMs excel in Go, but I am not sure it is the best tool for that job. A good ORM would go a long way to making that less true, but I'm not sure even that would put Go at the same level as tools that are geared towards that type of problem. Go, like all tools, is still targeted at a certain class of problems.
Not GP but my view is that Go excels in "split-backends" where you kind of have a front-end server and a back-end server where the front-end is taking in requests and passing them to a queue or interacting with services representing the "backend's backend". In this regard you're usually building for scale and high optimizing your code to be incredibly concise and to the point. An ORM doesn't belong here because you've already decided software quality > developer productivity within such an important capacity-driven system.
I find the majority of what you contribute to HN very insightful; I appreciate your activity here! I've yet to use an ORM that I've found added much or any value, and usually I have to fight it. I find "select id from user where email='foo@example.com'" much easier to work with than some odd query build that requires some DSL that made sense to some developer for their particular use case, like user.Get(id).Where().Email("foo@example.com). My most recent case of growing disdain for ORMs being SQLAlchemy. What ORMs do you find (in any language) that you like and what is it about them that helps you?
I miss generics, not because I would write new generic functions all the time, but because a few key generics provide infrastructure that increases the beauty of programs dramatically.
The tupled (T, error) return types are a key area I think generics would help. The error-tuples themselves aren't the problem, but rather Go's inability to cleanly handle them. Threading error state around requires repetitive glue code (if err != nil). In some languages, you hide the glue code in monads. In some languages, you hide the glue code in macros. In some languages, generics reduce the glue to chained methods on helper objects. In Go, once in a while you can hand-write a lambda function to manage a closed-over error variable and clean things up a bit, but that's about all you get.
I don't mind the repetitive glue code. It might be nice to clean it up a bit, but it's the least of my concerns with Go. When I miss generics, it's usually for some tree-like structure, but the cases where I need a high-performance tree structure and can't take the performance hit of `interface{}` are few and far between.
Indeed, but I think if they REALLY don't want to change the Go language, an officially blessed std library of "go generate"-able collections and algorithms would be an ok workaround for private use inside a package. It's extra work for the users, but you'd get widely-used, widely-tested, type-safe collections. The main problem would be interoperability when two packages independently generate an RBTree<int64,string> that Go thinks are different types.
> it's usually for some tree-like structure, but the cases where I need a high-performance tree structure and can't take the performance hit of `interface{}` are few and far between.
The problem with interface{} is that it makes it pretty much worthless to have a type system at all.
Absolutely not, they are not the same. Without generics, the compiler can't make any guarantees on the soundness of your code. Go will accept code that's inherently broken and that Java will simply refuse to compile. I like to catch such errors as soon as possible.
As for saying "they are the same once the code is running", well, duh, of course, but it's completely useless by then.
Although I very much like C# where generics are not elided, I think most of the value comes from the productivity from static analysis, and therefore Java generics cature most of the value. Perf improvement opportunities are great though.
> The problem with interface{} is that it makes it pretty much worthless to have a type system at all.
I don't understand this argument. You need `interface{}` about 1% of the time; somehow it's better to have no type checking than only 99% type checking? Even in the 1% of cases, I've literally never seen a production bug (or even a failed test case) caused by a type error. I'm sure they happen, but I doubt they happen so frequently that adding in a generic type system would be a net gain.
Except the 99% (though in my experience more like 80% of the cases where generics would be considered in other languages) of the time you are refraining yourseld from using interface{} and perhaps choosing another bad solution (lesser of the evils)
I don't think this is true in any meaningful sense. Perhaps you're talking about map() and friends, but these are so trivial that it's just as well to write out the for loop.
I miss generics, not because I would write new generic functions all the time, but because a few key generics provide infrastructure that increases the beauty of programs dramatically.
The problem is this: Have you ever worked in a mature codebase with generics, where they haven't somehow been overused? Please tell me where that is!
> You're seriously saying that generically? All languages should simply have them? Not so sure you should dismiss the context like that.
Point me to a successful statically typed language born after 2005 that doesn't have some form of generics. Go has generics by the way, you just can define your own in user land, which is a stupid limitation.
Almost any powerful new feature in any language initially gets overused, but that isn't a reason to ban it.
Class hierarchies, for example, got heavily overused before people discovered that composition most of the time is the better option, exceptions got overused before people learnt to only use them in exceptional cases, and dependency injection and XML (one of the first environments with strong support for it) got overused in Java programs.
Generics are going the same way, if they haven't already (JavaScript modules will get there, too, but that seems at least a generation of programmers away)
Also, the only difference I see between generics (with a bit of 'compile-time reflection') and go generate is that the former may be too magical for some, while the latter requires programmers to do more work (writing special comments to use existing functionality, and writing AST manipulation code to write new ones) and that it generates files only because the compiler wants to see them.
Having personally fallen into the trap of overusing C++ templates on a project, I understand the point. However, I did not go on to reject the idea that templates should be in the language.
Backing away from specific suggestions, I think it's healthy to recognize that Go has a boilerplate/DRY problem--not just with error handling!--, and it would be nice to address it in some way. If not with generics, then with something.
C++ templates! I keep on running into places where I'm debugging, then I find myself in a maze formed by template overuse, then debug around that instead.
just having parametric polymorphism
I think that could be workable in Go. I can imagine how debugging that would be straightforward.
> I have decided to get proficient in Go about 3 years ago and I am glad I did. One of the discoveries in the process was that Go enables you to do things that previously were extremely difficult and you'd typically not even consider as options.
> what it does couldn't possibly be done in e.g. Python
What can you do in Go that you can't do in other languages? I can't think of anything. The only major benefits over, say, C++ or Java (which I don't think are very impressive languages) are a few convenient threading primitives, but you also lose a lot of expressive power.
Efficient concurrent programming. I'm not aware of any cross-platform C/C++ or Python libraries that give you async I/O + multi-threaded coroutines. Such a library exists for the Java ecosystem (quasar), but IIRC, it's enabled by clever bytecode tricks, so it's not built with vanilla Java. Further, you still have to take care not to use any libraries that are incompatible with this concurrency model (e.g., anything that does sync I/O).
Precisely as I said, "The only major benefits over, say, C++ or Java (which I don't think are very impressive languages) are a few convenient threading primitives"
99% of projects don't need green threads. But for those that do, I'd much rather use a language better suited to the purpose, like Haskell or Erlang. This isn't a very convincing argument for Go.
Erlang isn't necessarily better than Go in this regard. Since Erlang is interpreted and everything has to copied between processes, Go is bound to be faster than Erlang, especially when it comes to parallelism. Go is also statically typed, which in my experience makes it easier to avoid bugs in large teams.
Erlang, however, is much better at distributed concurrency. There are few who suits this use case better.
Erlang is actually compiled, and binaries above a certain size are reference-counted rather than copied between processes. It still makes various compromises like using bignums and doing preemptive scheduling which make it slower in general.
It's compiled to erlang bytecode, yes, but not to machine code. You do have the option of doing that, but I do believe you miss out on live updates? Not sure.
Erlang is great though, especially Elixir, but for some use cases Go is better.
If the first paragraph is the extent of your concerns with Erlang (which are reasonable), they are more than addressed by Haskell. Compiled and very statically typed.
Yeah, but it's more difficult to teach others Haskell (what's a monad?) than Go. I know all three languages, I mostly prefer Go for backend work because it's easier for others to join in on.
Point is that they all have their strengths, but one is not necessarily better than the other. As with all things digital, it depends.
> Precisely as I said, "The only major benefits over, say, C++ or Java (which I don't think are very impressive languages) are a few convenient threading primitives"
I commented before your edit. The question I responded to was something like "What can you do in Go that you can't do in C++ or Java?"
> 99% of projects don't need green threads.
Perhaps not need, but many of those projects are paying a large opportunity cost in today's no-free-lunch, multicore, I/O-bound world.
> But for those that do, I'd much rather use a language better suited to the purpose, like Haskell or Erlang.
I dispute that Haskell or Erlang are better suited for the purpose. And good luck finding Haskell and Erlang developers.
Have you tried hiring Erlang or Haskell developers? It's not like they don't exist. In fact, there are likely many developers that would love to take an Erlang or Haskell job, but there aren't that many opportunities.
I don't dispute that they exist, I dispute the economics of finding them compared to finding or training new Go developers. Frankly, no one is going to replace their entire team and start a hiring campaign because Haskell or Erlang (arguably) make concurrency slightly easier than Go.
You would be surprised by the result. Economically it make sense. And i would argue that production ready Elixir/erlang is easier to teach than production ready Go.
Right, but this is still a far cry from Go's runtime/scheduler. In particular, the Go runtime completely handles threading and async I/O for me. I'm not sure what C++17 coroutines look like, but I don't even have to explicitly yield (which is the case in other C++ coroutine libraries).
I don't see how Go bears any resemblance to an async/await model. Did I misinterpret your statement?
> Also you don't need explicit yield on task runtimes like HPX.
I don't dispute that it's hypothetically possible to bolt enough things onto C++ to make it behave like Go, but I seriously doubt it's a viable solution. I would sincerely love to know if anyone has done all of this and is using it in production.
Async/await model is nothing more than a state machine over coroutine.
Goroutines are coroutines.
On the async/await model there are two approaches.
1 - The compiler takes care of the scheduling of the coroutines among the threads on the task pool, like C# 5.0 introduced.
2 - The compiler maps async/await into intrisics that know magic functions on the handles that represent tasks managed by the thread pool. This is called generalized async/await and is how C++17 will do it, and also how the C# model will evolve on C# 7.0
So while one must restrict ourselves with the co-routine scheduler provided by the Go runtime and to the yield points managed by it, with generalized async/await it is possible to have control how the scheduling actually takes place among co-routines.
Granted Go's model is easier to understand and might be enough for many people, but it isn't a solution for all types of concurrency problems.
Traditionally, "coroutine" has been used to refer to a userspace concurrency unit that is not multiplexed and which requires explicit yielding. Mostly I'm grumpy about broadening a previously precise definition.
> So while one must restrict ourselves with the co-routine scheduler provided by the Go runtime and to the yield points managed by it, with generalized async/await it is possible to have control how the scheduling actually takes place among co-routines. Granted Go's model is easier to understand and might be enough for many people, but it isn't a solution for all types of concurrency problems.
Not having to control your own scheduling is the selling point of Go's concurrency model. Besides, Go does offer (at least some) control over scheduling via the runtime library. I've never had to use this in practice, and I can't imagine a scenario that would require nuanced control. I don't think async/await are the worst things, but I still prefer Go's runtime at least for the problems I have to solve.
It's not that go can do things other language can't . It's the combination of keeping the language simple and gofmt means I can read anyone's code and understand it. I've been doing this for 25 years and that has never been true for me with any other language.
The real benefits for me with go are
Tooling. Gofmt, godef, etc make using emacs a dream.
Extensive stdlib. I pull in exactly two small libraries to build a very large web app: odbc, gorilla mux. Everything is baked in: http server, templating, compression, database
Cross compiling, being able to build a Windows exe in my Mac for deployment is awesome
Fast compilation.
Interfaces are a key concept and used everywhere to great effect in the std library
Of course there is almost nothing, which you cannot possibly do in any other language. The question is, at which effort and the resulting program quality.
Go gives you strict (vs C) static type checking. Its local type inferencing saves you a lot of typing (as in keyboard) and makes the code concise and thus readable. It has a proper package system and very fast compile times (for larger projects way faster than C and C++). It has a rather rich standard library, it has a great GC which gets even better in 1.8 (100 microsecond stop times).
Many of the "old" ways of thinking do not apply to Go, and I had to get over the phase of looking for an ORM or webapp or testing frameworks - they don't exist because they are not necessary, and those that do aren't any good really.
I ended up inventing my own challenge - a time-series database backed by PostgreSQL (http://github.com/tgres/tgres), because I needed a difficult problem. Tgres is still not fully baked, but what it does couldn't possibly be done in e.g. Python, and not in C in as little time (even though it's been two years in development).
Contrary to what the blogs say, Go is _not_ an easy language to learn. Interfaces, goroutines, channels, selects, etc. will take a long while to become second nature, and if you're not using them then you're not really getting anything out of Go.
With respect to all the criticism Go is getting out there, e.g. lack of generics - I honestly can't say there is anything that bugs me at all. Some things may take getting used to, but usually in the end you're glad to have done so.
As someone who has done a lot of programming in my life (in Python, C (remember mod_python?) and also Ruby, and (with displeasure) Java) I can say that Go is a big deal in the language landscape and will not long from now be as big as Python - in fact I think it is Python developers who are most likely to switch over to Go for their next project.