Hacker News new | past | comments | ask | show | jobs | submit login
Seven years of Go (golang.org)
303 points by 0xmohit on Nov 10, 2016 | hide | past | web | favorite | 312 comments



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 :

https://github.com/golang/go/wiki/SliceTricks

Just because :

> A weakness of Go is that any generic-type operations must be provided by the run-time

https://blog.golang.org/slices

Now what was your point about the "old" ways of thinking again ?


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.

Still, generics would be nice.


> 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.).

[1] https://en.wiktionary.org/wiki/deepity#English

[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.


> That sounds like a deepity[1] if ever I've heard one.

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.

Not sure what the rest of your comment has to do with my point, which was about, if you like, the pragmatics of programming.

Not concerned with the space of programs that type check. Insufferable haskellers...


> 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.


>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.

Idiomatic is only good if the idioms are good. Historically for lots of languages, and I would include Go there, the prevalent idioms were crap.

E.g. Java circa 2000-2007 and the J2EE factory-singleton-factory-proxy-singleton-XML crapfest.

Or PHP and the "devil may care" attitude of pre-Composer PHP codebases (including big ones like Wordpress).

Or the tons of broken ways of working with Javascript until the community regained some sanity in the last 5-6 years.


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.


If you're doing that all over the place, you're doing it wrong. I've never seen Go code that looks like that in real projects.


> If you're doing that all over the place, you're doing it wrong. I've never seen Go code that looks like that in real projects.

Yet it is there in the language. Why is that? how do you justify adding this to a statically typed 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.


> If you're such a poor programmer,

No need for personal attacks.

> then just find a different language.

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".


> C++16 beta compilers can compile the very first C program just fine.

nitpick: a c++ compiler can compile C only if it knows that it's a C source. Not every valid C source is a valid C++ source:

    char *foo = malloc(42);
this is legal in C, but not in C++. See https://en.wikipedia.org/wiki/Compatibility_of_C_and_C%2B%2B...

C++ is 33 years old. Still impressive backward compatibility track.

I guess that backward compat is a good reason for why C/C++ is still widely adopted.

IIRC there are some minor things that have been removed from the standard, e.g. the export keyword, but apparently it wasn't widely adopted if at all.

> ... This is why "gofix" exists I was under the impression that this was a thing before Go 1.0.


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.


So, Go with a more expressive type system is a worse Go?


>C++ doesn't have reflection

How would you describe RTTI?


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)


What is your argument?


>Classic. All languages stink, somewhere.

Which, as information is content-less.

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.)

[0]: https://github.com/stretchr/testify


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.


If you are doing plain SQL in your golang app, I highly encourage you to check out sqlx:

https://github.com/jmoiron/sqlx

It has features like directly unpacking the results of a query into a structure. Very handy.


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.


By far, it is dealing with migrations.

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.

[1] I use https://github.com/mattes/migrate


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.


I wrote something very similar, but this is exactly what I mean by writing 40% of an ORM.


I find this interesting. Are there examples around, or a blog post discussing this?


Also see https://github.com/go-reform/reform for a stable implementation of a similar idea.


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 with Go

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?


Wrapper functions are not the same as full blown ORM.

For every heavily used ORM features, there's an opportunity to fall into a trap, e.g N+1, because the surface area of an ORM is wide.

In contrast, wrapper functions are low cost, easy to write, and recognizable because you write them yourself.


> 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.


What domain are you referring to?


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?


>Strong, strong disagree on the idea that ORMs aren't necessary because of some property of Golang itself.

Might be interesting to hear reasons - by both you and the parent commenter.


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.


Performance is not the main problem with interface{}.


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.


Right, but it's a problem tagged-unions solve.


> 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.


Generics in java work via type erasure, so <T> is the same as interface{} by the time the code is running.


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.


That's not relevant to type safety, though. Haskell also has type erasure.


> 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.


exactly my experience


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!


I've also worked on codebases that have overused the && operator. Should we remove that in favor of nested if?


Only if the cost-benefit works out, and there isn't a better solution.


And that's clearly the case with generics.


You're seriously saying that generically? All languages should simply have them? Not so sure you should dismiss the context like that.


> 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.


As opposed to codebases that abuse interface {} and reflection ? I'd take the complexity of generics over this.


Overused in what way? Do you have an example of what you mean by that?

I'm having a hard time understanding where using generics would cause problems.

C++ templates, sure, but those are much more powerful than just having parametric polymorphism.


C++ templates, sure

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.


Yes, constantly. I've rarely seen generics overused.


Send me contact info.


Which is better, generics or macros?


> 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.

Such as?


> 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.

As in all cases. Use the best tool for the job.


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.

> This isn't a very convincing argument for Go.

I disagree.


> And good luck finding Haskell and Erlang developers.

Unless you need a massive number of developers, or you're located out in the middle of nowhere, it really shouldn't be too difficult.


I disagree.


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.


Still, I'm fairly risk averse. I'll wait for the inevitable explosion of Erlang/etc shops to take off before I jump on board.


It happened. But well. Do as you want. It is your project :)


C++17 gives you async/await, coroutines and parallel algorithms.


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).


Just like any other async/await model.

Also you don't need explicit yield on task runtimes like HPX.


> Just like any other async/await model.

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.


> Goroutines are coroutines.

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.


So what would you call coroutines that multiplex at API calls like NT fibers, or many green threads libraries.

Or for that matter, coroutines on Active Oberon, which are just multiplexed as in Go.


I would call them fibers and green threads, respectively. No idea about Active Oberon.


What about http://libmill.org ? There are also numerous other programming environments with similar constructs.


Not cross platform last I checked.


I guess no Windows support could be a dealbreaker, but it's definitely "cross-platform" in several senses.


That isn't cross-platform in the senses I care about. I'm sure libmill is great and I wish it the best.


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).


The two biggest thorns in my side with go have been rectified in go 1.8

Multiple result sets Sorting slices


My 2c: Go is incredibly convenient for non-programmers. I don't make a living writing software, I make a living answering quantitative questions, using computers. I use Python for most tasks, but when I need extra performance and/or portability, I grab Go every single time.

The main appeal is the quality standard library that covers most things I get in contact with. I could get immersed in it right away, because I didn't spend weeks trying to read the spec - the spec itself is so trivial it's mindblowing. The standard library feels very Pythonic in terms of reach. I've built tiny servers, parsed plethora of file formats, calculated maths, wrote data, (de)compressed stuff, encrypted things, ... The joy of not having to worry about 3rd party dependencies for all these is quite refreshing. And the appeal of few runtime issues thanks to static compilation is oh so excellent.

Sure, Go didn't invent any of these practices and sure, it doesn't have generics, runtime assembly, it's opinionated in a few places, its vendoring is subpar, ... But when you're someone who isn't really a programmer, these things don't matter much. I'm not here for the hype, I'm here for the productivity and ease of use.


> I've built tiny servers, parsed plethora of file formats, calculated maths, wrote data, (de)compressed stuff, encrypted things, ...

If you've written code to do all those things, I think you'd be justified in calling yourself a programmer, even if software isn't your main gig. :)


just curious, why write file parsers in go rather than say, python

i would think python would be easier.


Packaging in Go is bound to improve in the coming releases. You can follow Go Package Management list: https://groups.google.com/forum/#!forum/go-package-managemen...


Not only are you a programmer, you are clearly more aware and capable than many, many, people who consider themselves professional programmers.


Go is the VB, or the PHP of service development. Lots of obvious and immediate shortcomings, but with just the right amount of niceties for its domain in a very easy to teach and deploy package.


But with much more savvy language design than either of those.


If you are not a programmer, I don't know who else is.


Go isn't a perfect language, but it strikes the right balance for me between productivity and safety. There are few other languages right now with the same ease of deployment, good tooling, excellent standard lib, and raw performance. Here's to another seven years.


> strikes the right balance for me between productivity and safety.

Would be curious to hear what "safety" you're referring to, is it just that it has a basic static type system, (which is constantly being undermined by interface{} and reflection), or is there more to it?

Genuinely curious.


Compared to C it is safer. I never saw a segmentation fault, only some panic errors with bound checking.

I prefer go to python or javascipt. It's far too easy to make errors in them. Minimal static code checking.


> compared to C it is safer. I never saw a segmentation fault

Well, yeah, it has a GC so it kind of gets that "automatically", I was thinking more about the type system side of things. And compared to C almost anything is indeed safer.

> I prefer go to python or javascipt. It's far too easy to make errors in them.

That's fair, but these are both dynamic languages, so almost anything with any level of static typing beats them in this regard, I was interested if Go does anything beyond that.

Not to say these are not fair points, but nothing a whole host of other languages haven't provided before Go.


Not only static typing. I just found a bug in an open source python tool that crashed because of a missing member variable in a class. You have to assign a value in the attribute to create it. It requires careful programming to not get such errors. It is also easy to accidentaly create a member variable.

The time you gain by being able to hack some code together is lost by these types of error unless you are a very disciplined programmer.

Another kind of error with python is with blocks defined by vertical alignment. It is easy to make errors when you copy past code and the alignment is not corrected.

When you are under pressure or tired, your discipline may weaken. And this is difficult for inexperienced programmers.

You don't have these problems with go and the normalized formating helps a lot.


Comparing Go to C is like comparing C to assembly. You get segfaults because C is lower level and allows you to do things that are nearly impossible to do in higher level languages.

Better comparison is to compare Go to Java, also doesn't segfault, because it doesn't let you.


Since Go has pointers and Java doesn't. I thus expect Java to be safer than go. However, I don't know why but I don't like programming in Java. It's too verbose probably. I also prefer Go because it is closer to the metal and it's a very simple language.


I have seen segfaults in Go. But that's what I get for using gcc-go.


How is Go safer than, say, Java? It doesn't really do anything particularly useful or interesting on the safety front; it's type system is weak, it has null pointers, its error management mechanisms are mediocre.


It's not safer than Java. The selling point over Java is developer friendliness (simpler language, tooling, dependency management, deployment, etc).


I was asking the GP why he cited safety as a feature of Go. I agree with you; that's not a good argument.


I can't speak for the GP, but my interpretation is that safety is a feature of Go, but it's not unique to Go. Go is more safe than some languages, but less safe than others.


We can look at how many security updates Java release for a particular version. I see around 35 releases for Java 8 vs 6-7 release for Go during same period.


That's not what people mean when they refer to the "safety" of a language.

https://en.wikipedia.org/wiki/Memory_safety

https://en.wikipedia.org/wiki/Type_safety

http://tobyho.com/2008/03/30/how-safe-is-your-programming/

The fact that people consider "security releases" for compilers/runtimes to be normal and acceptable is actually extremely alarming to me, but it's different from language safety.


no. I really don't like java, but you really can't tell that go has better tooling and it is more developer friendly. it's an outright lie. java tooling is very mature and has solutions that no other language that I know can use. (Spock framework above all, it is written in groovy but it is perfectly integrated in java and still I haven't find anything nearly comparable in any other language). show me some BDD framework in go, just as an example.


> Spock framework above all, it is written in groovy

You're the one who's telling an "outright lie" (as you put it). I just downloaded and unzipped Spock from Github, and looked through its directory structure. Virtually all of it is written in Java, not Apache Groovy. There's only a few little files written in Groovy.

Perhaps you're confusing its source language with the language users of Spock write their test scripts in. In real life, Groovy is used for the JVM in the same way Bash is used for Linux, like writing glue code and build files. The statically typed stuff added in Groovy 2.x isn't used much at all, and virtually no-one would use Groovy to build actual systems. All software I know of that provides Groovy to users, like Grails, Gradle, and Spock, is itself written in Java.


Out of interest, can you point me to any GUI bindings or native Go GUI toolkits etc.? I would like to take a look, as I am normally writing native C++ GUI desktop apps.

EDIT: Thanks for all the info everyone. I hope my responses don't appear argumentative - I am genuinely curious.


I'm certain there are more, but here are a few

platform native https://github.com/andlabs/ui

gtk http://mattn.github.io/go-gtk/

qt https://github.com/therecipe/qt

qml https://github.com/go-qml/qml

shiny (still being developed, Go native) https://github.com/golang/exp/tree/master/shiny


Here is one - WALK:

https://github.com/lxn/walk

A friend had used it in a commercial project, and IIRC he said it worked okay. Not tried it myself yet. I think it wraps the Win32 GUI SDK but also has a declarative mode [1] (at least for GUI widget layout) on top, or something like that - only looked at some sample code briefly.

[1] meaning you can declare your GUI layout declaratively, but in Go code - it does not need XML or some other language or markup format like some declarative approaches do - IIRC.

I actually wish the Go team had built support for some / many of the major GUI toolkits on major OS's (at least) from near the start of Go's development. I realize that there could have been, or still could be, some issues with that, due to differences between Go and (C and C++) [2] models (object file format, calling conventions, stack and heap layout, etc. - basically compiler and compiler-output-related stuff), and hence it may not be easy to create bindings from the former to the latter. Not familiar with this area.

[2] since many of the major GUI toolkits are natively written in C or C++, so a Go wrapper would need to bind to them.

Not complaining though, I know what they (the Go team and community) have already done is a huge achievement anyway.


I haven't tried it thus far, but you can see a list at the Projects wiki [0].

[0] https://github.com/golang/go/wiki/Projects#guis-and-widget-t...


I'm not sure if this would work for you, but using something like Electron and having your go program output HTML/JS/CSS as your UI works really well with go (or have your go program output JSON or something to communicate with the front end)

If you throw something like react on there, you'll have a pretty simple functional UI, but if you don't want that there's nothing wrong with HTML/CSS and as little JS as you can get away with.


That sounds an interesting concept and useful for creating instantly-usable web apps but in effect isn't your Go app being relegated to a HTTP server? eg. how would I do callbacks without doing them as some request back to the Go "server" app? How would you create custom controls in this method? eg. if I wanted to do my own pie chart control/widget with popups etc.

It sounds like a long way around in comparison to what you can do in C/C++, ie native calls depending on the platform, or use an abstraction toolkit like wxWidgets. (Oddly C++ has done well without any native GUI toolkit of course, but at least there was native C ones to use).


Ideally you would put as much of the "business logic" in the go code as possible, and have it "render" to something like JSON which the front end just reads in and displays. Interactions with the front-end just send a new request to the go code to handle and respond with what is needed.

It is a lot more work for small applications compared to something like wxWidgets, but for larger applications the decoupling of the front end from the "back end" can help in a bunch of ways.

It lets you develop them somewhat separately, it gives you a uniform look-and-feel across platforms (could be a pro or a con depending on how you feel about it), and allows you to do something like replace the web-front-end with a native system later if you want (and you could write the front-end in the platform's native GUI widget library for best performance and integration while keeping the backend code identical).

So your go code is basically an HTTP server at that point, but it frees you up to let go do what it does best without worrying about how you will display it, and it lets javascript/html/css do what it does best (display stuff).

I've had great success doing this once before, and if you have some experience with React, it works out really "beautifully" as the front-end can be completely stateless. So what was pushed to it from go is what is displayed. Easy to test, easy to develop, and easy to replace if needed.


> Oddly C++ has done well without any native GUI toolkit of course,

Well from the OS vendors themselves we had MFC/WTL/Windows Forms/WTL on Windows, BeOS GUI, Symbian UI, CSet++ on OS/2, PowerPlant on Mac.

Additionally there was Turbo Vision on MS-DOS, OWL, VCL for Windows, Motif++ on UNIX.


I love Go and think it could be good in this area, but so far as I know no one is using it for any serious GUI work (yet?).


What is it being used for? HTTP servers? 7 years without proper penetration into the GUI market seems odd, unless it is aimed elsewhere?


IMHO Go is a good choice for lots of system daemons and commandline tools. If networking is involved it shines even more.

> 7 years without proper penetration into the GUI market seems odd, unless it is aimed elsewhere?

You could apply this criticism to nearly all languags, as there are currently only very few that are used for native GUIs: C and C++, because the operating systems and native toolkits are written in that, C# for Windows (Winforms and WPF) and Java for cross-plattform GUI applications.


Python has strong support for Gnome libraries, therefore GTK+ through GObject introspection. Go type system is too rigid to allow a tool like GObject introspection to interact with Go code. So no, this criticism doesn't apply to nearly all languages.


Might be true that Python has some advantages in wrapping GTK+ or even QT. But the parent mentioned "proper penetration of the GUI market". And I think also for Python the amount of GUI applications is quite low.


objc is a huge one! most iphone apps. increasingly, swift too.

there's some python stuff. the original bittorrent client. i did a couple apps using PyQt and python -- not an experience i would want to repeat, though.


Yeah, objc is. However mentally I counted that towards the OS languages and C family languages. Swift also belongs to the OS (first class langauge) category, although it's certainly not in the C family.

For others maybe Delphi was succesful in the GUI domain. But in the last years it was certainly not often mentioned. For GTK there's also Vala, which was probably even designed to be used for GUIs.


Oh yes, it's very much a an "os language". But an important one to include in your list, I felt!

Objc is certainly C-derived, but it's also just as much smalltalk-derived. Definitely a very different beast than plain C (more so even than C++, I would say).


>What is it being used for? HTTP servers?

Yes, that is one big area - networked servers - a superset of HTTP servers. The Google site that serves the Chrome browser is supposed to be a Go app, and I've read Google uses Go a lot internally. (I'm sure Rob Pike and his team must be using it, after developing it for that very purpose.) Reading up on the goals of Go will give some insight into that. Part of the reason was that they wanted it to write big systems at Google, including networked ones, and wanted a better experience than they had with C++ (build times [1] and also other issues). Rob Pike has written some about it, others may have too).

[1] This video that I blogged about:

Video: C++, Rust, D and Go: Panel at LangNext '14:

http://jugad2.blogspot.in/2016/08/video-c-rust-d-and-go-pane...

has many interesting language-related discussions; one of them is the discussion about the build times of C++ vs. those of more modern language. There are concrete reasons why C++ build times are relatively so large - the use of include files is part of the reason - those are discussed some in the video, between the panelists, including some of the language (co-)inventors, such as C++'s Bjarne Stroustrup, Go's Rob Pike and D's Walter Bright.

There is a nice moment or two near the end where Stroustrup responds about C++ build times - okay, won't spoil it for you and others - go see it :)

Command-line tools is another area where Go is used a lot. Getting the convenience of deploying a simple single binary (maybe at the most a config file or two with it) even by just copying to the deploy target - unlike what you have to do with interpreted languages like Python and Ruby (unless you use something like PyInstaller or py2exe for Python, but that is not officially supported and may or not work for all Python apps [2]). This gives us back some of the convenience of older compiled languages like C where this was the normal way of deploying - but with some benefits over languages such as C (see Go vs. C topics), plus goroutines, channels, etc., I guess. Though I have not come across any examples of what things command line apps using goroutines and channels could be used for - it would be interesting to hear/read about some. I mean apart from a few somewhat obvious ones such as a web client to download multiple URLs in different goroutines, concurrently.

[2] I've tried out PyInstaller on a few Python apps, including command-line apps, and a simple wxPython app or two - it did work okay for those, in the limited testing I did.

Edit: fixed typos, added a few points.


What's Coming in Go 1.8 [0] lists some upcoming changes in Go 1.8 (scheduled for release on February 1, 2017).

[0] https://blog.tylerchr.com/golang-18-whats-coming/


So, i was a Go developer for ~4 years, then for the last 4 months or so i've been learning and using Rust. The pure joy of some things with Rust was astounding.

Now, i joined a new job and they're in need of a new language for some backend tasks - the choice was mine. Rust or Go? The backend tasks were heavy API servers - nothing amazing, we don't do groundbreaking work. Python was their existing language, but they weren't too happy with it.

For weeks (literally) i mulled it over, i really really really wanted to use Rust. Rust gives me a peace of mind that Go never came close to. Unfortunately, Rust does not fit the development speed nor developer experience[1] that this shop has.. and i really felt Go would be the best choice for this place.

My point to this post isn't to make a Rust vs Go comparison.. but rather, a wishlist for the hopefully eventual Go 2.0. I have no idea if/when that will ever be made.. but i hope it will, and i hope they adopt some things from Rust.

1. Channels without panics. Channels are awesome, but Go's design of them means that you have to learn special ways to design your usage of channels so that it does not crash your program. This is asinine to me. So much type safety exists in Go, and yet it's so easy for a developer to trip over channels and crash their program.

2. Proper error handling. I love error checking - i love it in Rust especially. It's very explicit, and most importantly, very easy to check the type of things. Recently i was reading an article about Go errors[2] and it made me realize how messy Go errors are. There are many (three in that article) ways to design your error usage, and worst of all your design doesn't matter because you have to adapt to the errors returned by others. There is no sane standard in use that accounts for the common needs of error checking.

3. Package management. It's a common complaint, i know. But Rust & Cargo is so excellently crafted.. Go just got it wrong. Recently i've been using Glide, and while it's a great tool, there is only so much it can do. It's a tool built to try and put some sanity in a world where there is next to no standardization. We need a Go package manager.. hell, just copy Cargo.

4. Enums. My god, Enums. Such a simple feature, but so insanely welcome and useful in Rust.

You'll note that i didn't list Generics. I know that's high on peoples list, but not mine. To each their own.. please don't start a holy war. (this is likely due to me using Go for ~4 years. I'm quite comfortable without Generics)

[1]: By experience, i mean what they desire/expect. They're a Python shop after all, i didn't want to make a choice that would cause them to spend weeks/months feeling unproductive. [2]: http://dave.cheney.net/2016/04/27/dont-just-check-errors-han...

edit: minor text fixes.


"Channels without panics. Channels are awesome, but Go's design of them means that you have to learn special ways to design your usage of channels so that it does not crash your program. This is asinine to me. So much type safety exists in Go, and yet it's so easy for a developer to trip over channels and crash their program."

I've solved this in my programming by finally coming to grips with the fact that channels are unidirectional, and if you want any bidirectional communication whatsoever, up to and including the client telling the source for whatever reason it has closed, even that one single bit, that must occur over another channel of its own. Clients must never close their incoming channels. This does mean that many things that Go programmers sometimes try to bash into one channel need to be two channels.

But I agree it's a problem.


> I've solved this in my programming by finally coming to grips with the fact that channels are unidirectional, and if you want any bidirectional communication whatsoever, up to and including the client telling the source for whatever reason it has closed, even that one single bit, that must occur over another channel of its own. Clients must never close their incoming channels. This does mean that many things that Go programmers sometimes try to bash into one channel need to be two channels.

Erlang got it right (again), by sending messages to processes they can only be unidirectional.


I see Erlang and Go as duals of each other here, at least considered locally; Erlang focuses on the destination and Go focuses on the conduit of the message. Each have advantages and disadvantages. I think Erlang's approach ends up easier to use, but you lose the useful ability of channels to be multi-producer and multi-consumer.

(I'd like to see Erlang create a concept of a multi-mailbox where you can have a PID that can be picked up by a pool. Trying to create pools from within Erlang proper is quite challenging, and the runtime could do better. I acknowledge the non-trivial problems involved with clustering; I think it'd still be a big improvement even if they only work on the local node.)

The Erlang equivalent is that messages don't carry their source with them, so you must embed the source in the message if you want to send a message back. The main difference here is that Erlang doesn't offer anything that can be misunderstood as sending back a message any other way, so nobody is fooled and this never comes up as a problem once someone gets Erlang. The problem in Go is that the client end of the channel is capable of closing the channel, even though it really seriously never should.


I don't know how, but frankly i'd love to eliminate all simple panics. Nil pointers and channels seem two big culprits, offhand.

Granted, i left out nil pointer/interface panics because it seems unrealistic given how difficult it was for Rust to get rid of nil pointers. I'm not sure Go 2.0 could do it and still be considered Go.


> Granted, i left out nil pointer/interface panics because it seems unrealistic given how difficult it was for Rust to get rid of nil pointers.

That wasn't really a difficult part, it's just a basic application of generic enums.

Now Go doesn't have (userland) generics or enums, but they could have taken the same path as other languages (and the one Go is wont to take): special-case it. Which they probably can't anymore because of zero-valuing, you can't have a zero value for non-null pointers.


I agree; I want non-nillable types in Go. This despite the differences in Go that makes nil values more "valid" than they often are in other languages.

I still faintly hold out hope. Unlike many of the complaints about Go that would require fundamental restructuring, C# showed that actually can be retrofitted onto a language without breaking it.


> Unlike many of the complaints about Go that would require fundamental restructuring, C# showed that actually can be retrofitted onto a language without breaking it.

C# added nullable types. Not non-nullable types.

In Go, the concept of zero values is so fundamentally baked into the semantics that non-nil pointers can never really be added to it.


I'd just make my own uninitialized type in Go. In fact, I think I've done it. I'd guess you'd want to avoid the boilerplate.


> Granted, i left out nil pointer/interface panics because it seems unrealistic given how difficult it was for Rust to get rid of nil pointers.

That wasn't difficult at all.


Isn't that a timing race? You're relying on the notification of "I'm done" getting there before the other thread tries to send.

Write-and-panic has the advantage of being atomic. And you can catch a panic.

If you have a stoppable reader like that, maybe have it pull using a channel of channels. That is, consumer sends the producer "here is a one use unbuffered channel, please put an item in it" and then blocks waiting for the response.


I don't think panics on channels are a big problem. Mostly they are a symptom of bad architecture. In a good design the ownership of each end of the channel is exactly defined, similar like you would have to define the ownership for all resources in Rust. As soon as that's the case the owner can (on demand) close the channel and others can read from it and wait for the close/finish.

The only corner case is multi-producer channels, which either need additional synchronization or could be left open and garbage-collected.


I've taken a very similar path recently myself and started looking into Rust.

> 2. Proper error handling. I love error checking

Hugely agree here. I can get behind Go's overall mentality of returning errors instead of throwing exceptions, but in my mind there are not enough primitives in the language to keep this style of coding safe and sustainable.

Rust's `Result` type, `try!` macro, and pattern matching are an incredibly powerful combination, and make Go's approach look downright medieval in comparison.

Overall though, Go is probably still the more practical choice between the two languages (due to Rust's incredibly high barrier to entry).


> Overall though, Go is probably still the more practical choice between the two languages (due to Rust's incredibly high barrier to entry).

Does Rust really have an "incredibly high barrier to entry?"

I've been using Rust for a few months, and just deployed my first high-throughput application a month ago, and my experience has been the opposite. Yes, the first couple of weeks were a bit rough while I was getting used to the ownership system, but since then I have been progressing at a relatively quick pace. The package and dependency management facilities are incredibly good, and I've found high-quality libraries for nearly all my initial needs.

Compilation times could be faster, but the error messages provided by the compiler are so useful that I have come to depend on compilation errors for refactoring. The gains in predictable performance and resource utilization have far outweighed any initial cognitive overhead in the learning process. The community and the resources they provide are fantastic.

Coming from a mixed dynamic language and functional programming background, I could see room for improving certain FP aspects of the language, but am impressed with the pervasive pattern matching and collection handling.

Not a knock on Go, but rather an endorsement of Rust and its future.


> Yes, the first couple of weeks were a bit rough while I was getting used to the ownership system, but since then I have been progressing at a relatively quick pace.

This is the very definition of "high barrier to entry". Clearly it wasn't too much of a barrier for you but I can see how it'd be an issue for people. I'm expecting editor support and wider adoption (differently constructed tutorials, SO answers) to lower this barrier. I think Rust has the potential to be very popular, particularly if the reputation shifts from "high barrier to entry" to "slightly harder to get started but fewer problems in production".


I can see that, but a couple of weeks of investment doesn't seem "incredibly high" to me.


It took me about a week of fairly vigorous effort to start writing it fluently, but I also have the advantage of having seen and written many other programming languages. I have a few anecdotal examples of friends who are great developers, but still have trouble with Rust's ownership system.

When I say something like "incredibly", it's after thinking about trying to teach it someone more junior (like you'd see in a corporate environment with a mix of skill levels). I think that this would be a very difficult task.


> Overall though, Go is probably still the more practical choice between the two languages (due to Rust's incredibly high barrier to entry).

It depends a lot on your use case. I wouldn't say that Go is unconditionally more practical: there are cases in which you just can't use it.


Indeed. But looking at the GC improvements in Go 1.8 ("typical" GC pauses of less than 100us), the set of cases where you can't use the language might be shrinking significantly. Now if only they could turn that in some sort of guarantee...


The sole performance metric of GC is not pause times! Throughput matters just as much if not more!

GC pauses are not the only reason to use Rust. Rust is not "little Go" that you reach for only if you don't want GC. You might want package management, data-race-free concurrency, a mature optimization framework, runtime-free operation, concurrent data structures, fast C interfacing, etc. etc.


You're right of course about the GC metrics. And there was some disappointing increase of GC CPU usage with the 1.8 changes (I don't now the current status). But some of the items you mention (package management, mature optimization framework) will hardly make cases where Go cannot be used, which was the original point. Same for concurrent data structures which can be implemented in Go, even if the lack of generics makes it less convenient. I do agree with you regarding the other items.


Aggressive compiler optimizations are not optional in many domains.

One rule of thumb that a lot of people don't realize is that if you aren't maxing out your sequential performance, your parallel multicore algorithm usually loses to an optimized sequential one. The reason is simple: parallelism introduces overhead, leading to guaranteed sublinear speedups. Compiler optimizations, on the other hand, frequently result in multiple factors of improvement.


Right, but I think those cases are few and far between. Basically where you need blistering performance and/or deterministic resource usage. I would throw "safety" in there, but many (most?) safety-critical applications are written in C, and Go is certainly safer than C.


> Right, but I think those cases are few and far between. Basically where you need blistering performance and/or deterministic resource usage.

No, there's a lot more. See my reply to your sibling comment.

> I would throw "safety" in there, but many (most?) safety-critical applications are written in C, and Go is certainly safer than C.

This argument doesn't make any sense to me. Why is being better than a language from 1978 our sole criterion? Shouldn't we try to make our software as reliable as possible?

Besides, Go is not any safer than C when it comes to data races. I care about those a lot too.


> No, there's a lot more. See my reply to your sibling comment.

Which criteria from your post can't be rolled up into performance, deterministic resource usage, or safety?

> Why is being better than a language from 1978 our sole criterion?

I have no idea. Thankfully no one made any such argument.

> Shouldn't we try to make our software as reliable as possible?

No, we should make it sufficiently reliable. For example, many applications might not benefit from Rust's pedantic checking of data races (e.g., if the application is sequential), but the impact on development velocity could be prohibitive. By the by, I like Rust, I just think it's not well-suited for most applications.

> Besides, Go is not any safer than C when it comes to data races. I care about those a lot too.

While I absolutely agree, this isn't incompatible with my claim, that Go is at least as safe as C, and thus safety alone doesn't preclude Go from safety critical applications.


> Which criteria from your post can't be rolled up into performance, deterministic resource usage, or safety?

Package management. Macros (Rust has a widely used ORM). Generics. Easier error handling. Pattern matching. Functional features, such as map(). A more flexible module system. Built-in FFI. Inline assembly. Etc, etc.

> By the by, I like Rust, I just think it's not well-suited for most applications.

I'm going to push back on this too. In terms of "all programs that anyone has ever written", scripting languages are overwhelmingly the most suitable choice. But in terms of usage, core infrastructure favors reliability, performance, and interoperability with other languages. Consider regex libraries, language runtimes, graphics libraries, codecs, text/internationalization support, windowing systems, UI libraries, browsers, and so forth. This is our core infrastructure, where performance and reliability are paramount, and these libraries have outsized importance.

> this isn't incompatible with my claim, that Go is at least as safe as C, and thus safety alone doesn't preclude Go from safety critical applications.

This assumes that C is OK for safety critical applications. It's not. The status quo is bad. The bar should be much higher.


> Package management. Macros (Rust has a widely used ORM). Generics. Easier error handling. Pattern matching. Functional features, such as map(). A more flexible module system. Built-in FFI. Inline assembly. Etc, etc

Package management and easier error handling are the only ones that doesn't roll up into safety or performance, and I dispute that Rust's error handling is easier than Go's.

> I'm going to push back on this too ... our core infrastructure ... libraries have outsized importance.

Granted, but that's not "pushing back" on my point, because important though those libraries may be, they still don't constitute a majority of applications.

> This assumes that C is OK for safety critical applications. It's not. The status quo is bad. The bar should be much higher.

Agreed. Rust is better than Go for safety critical applications--note that I never tried to argue the opposite--only that Go is better than the status quo, terrible though it may be.


> Package management and easier error handling are the only ones that doesn't roll up into safety or performance, and I dispute that Rust's error handling is easier than Go's.

Generics, pattern matching, functional features, modules, easy FFI, are productivity features! They have nothing to do with safety and performance (although they are incidentally used to implement some speed/safety features, because why not). Inline assembly and FFI are useful for platform interop: again, these are not safety features.

> Granted, but that's not "pushing back" on my point, because important though those libraries may be, they still don't constitute a majority of applications.

I suspect the numbers are like: 95% of applications are best in scripting languages. 4% are best in managed languages (Java, C#, Go, etc.). 1% are best in low-level languages like Rust. If you want me to concede that point, fine. But in terms of importance of projects, which correlates with the number of developers you need on the project, the numbers look very different.


> Generics, pattern matching, functional features, modules, easy FFI, are productivity features! They have nothing to do with safety and performance

I dispute that these features improve productivity. With the exception of modules (whose purpose has always eluded me) all are available in Go (or could be trivially implemented) at the expense of safety and/or performance.

> I suspect the numbers are like: 95% of applications are best in scripting languages. 4% are best in managed languages (Java, C#, Go, etc.). 1% are best in low-level languages like Rust. If you want me to concede that point, fine.

I disagree. I think that Go beats scripting languages at their own game, in most cases. Of course this is all opinion and mine is no more valid than yours.

> But in terms of importance of projects, which correlates with the number of developers you need on the project, the numbers look very different.

Perhaps, but I'm not sure what your point is here, or how it relates to the broader topic. Are you arguing that more developers are needed on the project and thus more developers in total work on these core libraries than all other software projects?


> I dispute that these features improve productivity. With the exception of modules (whose purpose has always eluded me) all are available in Go (or could be trivially implemented) at the expense of safety and/or performance.

OK. How do you implement array.map() in Golang?



Wow. That seems very complicated for an implementation of map().

It seems like this is a perfect example of how generics are a productivity feature.


It's complicated because the reflect library is very general. There's no need to repeat most of this. So in fact, it's a perfect example of how functions are a productivity feature.


I would argue it's both.


> Besides, Go is not any safer than C when it comes to data races. I care about those a lot too.

Agreed. It may be safer in the realm of "oops i forgot to free memory", but i've had more panics than i could count in Go.


More importantly, it's safer in terms of using only valid memory.


Choosing Rust in a shop where no one knows Rust is indeed a big mistake where in Go it's pretty easy to pick up the language quickly.


My experience is the exact opposite. Most of the people who I work with now using Rust did not know any Rust at the time they were hired. There haven't been any major problems at all.

Do you have experience that shows otherwise?


I'm a former C++ dev. I've made repeated attempts to learn Rust over the last few years. I now feel like I'm capable of building things with much effort and frustration and constant Googling, but with a small fraction of the effort. By comparison, I got to the same point in Go in an afternoon.


I'm not interested in debating which language is easier to learn for C++ programmers. I'm pushing back against the (clearly false, IMO) idea that adopting Rust is a mistake.


In the context of the conversation, adopting Rust over Go is a mistake for the majority of applications primarily because of the difference in learning curve. Of course there are other factors, and not all applications are equal. I threw in my C++ background to demonstrate that I'm quite capable of thinking in the low-level terms demanded of Rust programmers, and my learning curve was still very large. You can disagree if you like, but my comment was related.


I don't agree that the learning curve difference (which is a temporary cost that decreases over time) is high enough to outweigh the benefits in the "majority" of cases that could benefit from Rust (and reap those benefits long-term). I respect your experience, but it doesn't invalidate mine, which has held up with many people I've seen get up to speed with Rust.


I never claimed my experience invalidated yours; you asked for experience that was contrary to yours, so I offered mine.


It's about the learning curve, especially people coming from Python to build websites, I mean seriously you would recommend Rust over Go to someone that build api / websites? It's telling someone that used to Ruby to go the C++ way, terrible idea for Python/ruby.


> I mean seriously you would recommend Rust over Go to someone that build api / websites?

Depends on the Web site. I wouldn't particularly recommend either for most CRUD apps.


Out of curiousity, why were the only choices Go or Rust? The type of thing you describe sounds ideal for Scala, Java, Kotlin, C# ...


I had to pick something i was familiar with too. We need this on a shorter timeframe (when is that not true? haha), and evaluating a language is hard.

As it is, this was a partial reason against Rust for me. After my time learning Rust, i'm still not 100% confident in my ability on it. My lack of experience and thusly productivity-hit is a real factor i had to calculate. Moreso with the rest of the team.

With Scala/etc i would be learning it new myself, and it was a gamble i wasn't going to take. Especially because our application is not intensive to specific problems that some languages excel in (concurrency, etc).

I'm sticking my neck out choosing a language, so i wanted to be quite sure i make the right choice for us. Well, as best i can - i am new in this shop after all.. i hope i made the right one :)


Best of luck with the project! :)


Honestly, unless you need some special library or your team is already familiar with those languages, I would pretty much always recommend Go over those languages. Everything is just simpler, and it's at least as fast as any of those languages.


> 3. Package management.

This is being taken care of. A package management committee has been formed and they will come up with a prototype tool in Go 1.8. https://docs.google.com/document/d/1qnmjwfMmvSCDaY4jxPmLAcca...

> 4. Enums.

Doesn't the iota identifier solve it for you ? It pretty much covers all my enum use cases.


> Doesn't the iota identifier solve it for you ?

They're talking about sum types (aka tagged unions aka variant record), where you have a proper type with a closed set of variants (which can be checked for completeness by the compiler) and optional associated data (possibly of different types for each variant).

Iota isn't even a step up from C's enums, it doesn't come close to what you can find in ML descendants (such as Rust or Swift for the most recent ones).


Curious what they weren't so happy about with Python? Was it purely performance? If so, did they consider PyPy, or at least profile what the slowest bits are so they can evaluate whether to throw everything out or just rewrite the slow bits? Was it the language itself? Not everyone likes dynamic languages, though it's odd they started with it. Did you consider Node at all?


From my time doing server backend python dev, it is only catching any problems at runtime, everything from missing arguments to typos in variable names that accidentally match another variable, turning your int to a string. Having a compiler catch these saves much time and hairpulling. And having unit tests as a final defense, rather than the only defense, does wonders for my peace of mind.


As a Python user and fan I hear this complaint a lot. I understand but I can't really agree since things like typos and type fails pretty much never happen to me, at least in production. My secret? I use the REPL, heavily. (And not even in the grand Lisp fashion, because Python's REPL isn't very advanced, mostly I use it just off to the side and maybe or maybe not running an instance of the full program, or parts of it.) Using the REPL catches most of those things just as quickly as a compiler, plus it can catch things compilers don't, such as null pointer exceptions.

Two lesser secrets are using a linter, which catches all sorts of issues too, and second actually getting the full program locally to a state where I can have it execute (most of) the new code I just wrote that I didn't verify in the REPL, or using data sources I didn't just define temporarily in the REPL, so I can make sure it seems to do what I intended. A lot of devs don't seem to do the second bit... Checked in code for Java compiles and passes existing tests and went through a basic code review but inevitably bugs get filed because it doesn't actually do everything the story said, it's like they didn't even try out their own code, it just looked correct and the compiler/tests agreed.

I think when you're working with the REPL interactively instead of relying on the common "edit -> save -> compile -> ship it|start over" cycle you don't miss those details as much, because you're constantly trying out your own code. Maybe my experience is because I don't typically use dynamic languages as scripting languages, at least in the sense of quickly hacking up a script, saving, getting to skip the compile step (look how much faster it is to develop in dynamic languages!!!), and running it until it works. I have done that, but even then, I'm usually writing the bulk of the script in the REPL -- or rather in my editor that can send text to the REPL. It's quite different from what seems to be the thing that made these languages popular to begin with, which is not having to explicitly type everything and getting to skip a (potentially long) compile step (which also encourages more source sharing).


> I understand but I can't really agree since things like typos and type fails pretty much never happen to me, at least in production.

The typos / type fail comments are shorthand for the real complaint, which is that a long-maintained large dynamic language codebase requires continuous vigilance. I've worked in dynamic languages for most of my career (Python, JS, Clojure) and typos/type-fails are pretty rare but if you haven't spent a half day tracking down a bug that turned out to be one of these at some point in your career, I'd be quite surprised. The breakdown comes when someone not familiar with the code makes a change without fully considering the consequences and it goes through something that nil-puns so the error isn't detected immediately.

My experience with people who are really against type systems is that they haven't run into a language with a good type system. I'm a fan of the gradually typed JS dialects (Flow more than Typescript) since you get the quick hacking at the beginning combined with compiler-enforced vigilance once you switch over to maintenance mode. Type-inferred languages are also nice, particularly fully type-inferred languages. I think F# [0] is both terse and accessible, for example.

[0] https://fsharpforfunandprofit.com/why-use-fsharp/


I see nothing false in rewording your statement to "a long-maintained large codebase requires continuous vigilance". Typing doesn't seem to matter with this. At least to the extent that we believe typing doesn't have a meaningful impact on the expected size, lifetime, and complexity of a codebase to solve an arbitrary problem. (With better type systems around it is neat to see statically typed languages quite significantly narrow the gaps in expressiveness though, and in some cases beat out 'trivially dynamic' languages.)

I know I've wasted time tracking down simple issues a static type system would have caught (or just due diligence by the coder -- and some of these issues I've caused myself! Though I really can't remember any insidious to find but quick to fix typo or type fail I caused, but I'm willing to admit to a possible selective memory bias), I've also wasted time tracking down simple issues a type system wouldn't catch -- even ones like Rust's, Haskell's, and dare I say maybe even Shen's? I also spend/waste a lot of time, probably the most time in total, tracking down complex issues that got past the type system and existing tests and code reviews and personal or team diligence, and these days most often in either Java or JavaScript, neither of which are particularly great poster children for their respective type systems. (I don't want to get into the strong/weak axis.)

Issues from NPEs or divide-by-zeros or undefined function calls, or stuff that goes through the type system's mechanisms to escape the type guarantees like reflection, casts to Object, void * , unsafe, serializing class names instead of values, etc., are annoying, a sudden power outage is also annoying. Some of that can be caught and prevented by more powerful languages, but still the time to fix those is nothing compared to more complex issues resulting in all sorts of incorrect behavior. There are so many more causes than type mismatches. It seems in your career the trivial bugs from typos are rare for you too. I'm not convinced the possibility of slight inconvenience those rare issues can create is worth the certain tradeoff in losing expressive power (especially if I can't use the most expressive static languages for whatever non-tech reasons) and possibly more, nor am I convinced a static approach is even the best one when you have languages like Lisp which support types well enough to error out when you call COMPILE on a bad form but still have huge flexibility.

I wonder if all this sounds like I'm a diehard C++ fan and don't need no stinking safe memory management tools because I never get segfaults or security problems. If it does I don't think it should, but it's really hard to explain why my perceived utility of static type systems is low without just appealing to preference, firsthand, or wise authority's experiences. The argument has been going on for decades by smarter people than me on both sides. In the end maybe it's just preference as arbitrary as a favorite color but rationalized to kingdom come. I at least don't draw my preference line so narrowly at static vs dynamic, there are plenty of static languages I'd use over JavaScript, and plenty of dynamic languages I'd use over C++.

I will ask about your experience though: how does it square with people like the author of Clojure? Is he just a god-tier outlier? I don't think one could argue he hasn't done his homework, or doesn't have enough real-world experience. It reminds me of a quip graphic I saw once, it was something like a venn diagram showing an empty intersection of "dynamic typing enthusiasts" and "people who have read Pierce's Types/Advanced Types books".


> I also spend/waste a lot of time, probably the most time in total, tracking down complex issues that got past the type system and existing tests and code reviews

Nobody will argue against you on this. The static/dynamic debate, as I understand it, is about whether typing and design constraints typing impose are worth the reduction in simple issues. Reasonable people can choose both and my personal take is that I think types are worth it given a good type system for a long-maintained project. You get more people coming on board and I think types are most useful in that situation.

generics and sum types

> how does it square with people like the author of Clojure? Is he just a god-tier outlier?

I happened to be in a group Rich joined for lunch at the last Clojure Conj and we talked about typed functional languages. The short story is that he doesn't think types are worth the tradeoffs. At the time Clojure had just introduced transients and he mentioned the numerous typed functional language blog posts about the feature and that most were incorrectly typed. He gives specific examples at the end of Simple Made Easy and reiterated many of them those during the discussion.

I think we'd agree that, libraries and architecture matter more than language but I do think language influences the abstractions the library can provide. My post wasn't really meant to argue the superiority of types in all situations but rather a specific response to the sentiment of "why do people always bring up typos when I never run into them"?


> plus it can catch things compilers don't, such as null pointer exceptions.

Note, that it depends on the language. In the case of Go, yes. In Rust? Definitely not.


Type safety mainly, i think. Performance is a definite concern, but they have a lot of internal applications and the stability of them varies. I offered up that less dynamic languages would provide more speed and reliability to boot.

I know Python got types in 3.5, though i'm not sure if it has Go-like Interfaces (Traits in Rust). If not, i think it really should.

I do firmly believe they'll be quite happy with Go though. Rust, not so much.


Seems a lot of the Go fans I read are former Python users burned by dynamic typing, so I agree they'll end up happy (or at least happier than Rust) with Go. Though one more option you might want to consider is Nim: http://nim-lang.org/ (It's pretty easy to get up to speed in it, especially for a Python user so long as they're not expecting to use fancy OO features.)


Python has always had Go-like interfaces in practice. The problem was that they were not reified into the code, so you had no easy way to know when calling a function and passing it a "file" exactly what file-like things the function was going to do with that "file" without reading the source code. You had to extract the interface yourself.


Not only that, but there's simply no guarantees. You can abuse a function in any way you see fit in Python, and the only one that suffers is your runtime sever :(

Optional types in 3.5 look awesome - but i don't want to lose duck typing. I want Go-interfaces in Python.


For me, it's the extremely straightforward conventions of Go, with it's straightforward tooling, and it's strong typing.

I "grew up" on Python, wrote a lot of code in it, and love it. But it doesn't feel as cohesive as Go.

As an example of cohesive tool design, let's look at Go package management. In Go, if I want to install a package, I install it with:

    $ go get github.com/pkg/term
Having installed this package, I import it in my code with:

    import "github.com/pkg/term"
Having imported this package, I'd like to read the documentation for it. To do that I use the command `go doc` with the package name:

    $ go dock github.com/pkg/term
Now that I've read the docs, I've got a question about how some particular functionality is implemented. With Go, I happen to know exactly where I can read that code, on my own hard drive:

    $ cd $GOPATH/src/github.com/pkg/term
With Python, I find that I don't have this absolute guarantee of consistency. Usually, packages will have a similar convention, but some require installing with one name and importing with another, and the local documentation viewer (pydoc) isn't installed by default, so I didn't even know about it until relatively late in my use of Python. I've had a similar experience with the rest of Python's tooling: it's as feature complete or better than Go's, but it's not quite as consistent as Go.


It was pretty bad that easy_install came out with no easy_uninstall. Plus some packages are in your system's package manager (which I think is great because I'm sick of every language having its own package manager when my system's (Gentoo) is better) and some aren't, or the latest versions aren't. Plus there's the virtualenv stuff, or the general problem of your dev environment not matching the deploy environment. Needing to have both Python2 and Python3 on your system in some cases. Some packages have C/C++ code so you need a compiler, and all the dependencies that implies. On Windows I think Python development is a joke, last time I did anything extensive there I think I ended up installing Enthought's distribution and picked off from http://www.lfd.uci.edu/~gohlke/pythonlibs/ as needed. I don't see how the Go situation on Windows could be worse than that.

I'm not a huge stickler for non-local consistency -- one of the things I like about Nim is its apathy about naming conventions (foobar is the same symbol as foo_bar or fooBar, func(arg) is the same as arg.func()...) -- so that's probably why I don't find the consistency factor a huge issue. When a language and its ecosystem has it, it's nice, but when it doesn't, it's not really a thing that annoys me.


Can't speak for the OP, but when you go full type checking it's hard to go back. Our infrastructure has many pieces in Python (right now I'm rewriting some) but all new APIs are in Go. The amount of trouble you don't even get to fight with type checking is huge. Performance gains are also good in many cases. Slightly more verbosity is a minor price to pay.


4. Enums. My god, Enums. Such a simple feature, but so insanely welcome and useful in Rust.

Enums are so underrated. So many architecture problems introduced by bad coders in Smalltalk would have been entirely avoided with Swift style enums.


Can you give examples, assuming you are referring to patterns that still are somewhat common? I've seen a bunch of somewhat-connected Singletons used as enums with extra functionality in Smalltalk, is that the kind of thing you were thinking of?


Most of the times we couldn't do the refactorings/changes we wanted to, it was because we could only be 99.9% sure and not 100% sure someone didn't stick some goofy value somewhere to denote something special.

Also, I'm not so sure that Smalltalk as a language community and as a programming environment did what it took to get everyone to do the right thing. In Swift, it seems like the programmer would quickly learn that an enum is the right thing to do, and management would be on board with using it, because it's obviously the way you're supposed to do the thing. I could just hear one of my bosses saying, "Do you really need that? I don't want to hear about you delving into some sort of 'science project' where you could just stick a String there."

The quick and dirty way of doing things with "somewhat-connected Singletons used as enums" would be just to use the class hierarchy, and subclasses of a particular class would constitute one particular enum. But even with that, someone would have some sort of objection to it.


Interesting point about refactoring and "doing the right thing", didn't think about that angle, thanks for the reply!


You're welcome. The stuff that's actually important in production code over years is often something hard to think of ahead of time while looking at toy programming examples. And often, it involves what humans might do under deadlines.


I solved channel without panic by doing channel bookkeeping in the main() entry point.

Wholeheartedly agree with you that channel panics a little too easily.


i love go, but follow rust with a lot of curiosity. a very interesting language, and much more ambitious than go.

when i've played with it, one thing that was always a big turn-off for me was super-long compile times.

as someone who used to do a lot of c++, i am loathe to go back to a language with long compile times, no matter how good the language...


Have you tried D?


i have not. should i?


You should definitely try it as it's easy to interface with C/C++ and the compile times are short, maybe comparable to Go. Also they fixed a lot of C++ annoyances and the language is older and definitely more mature than Rust. It's garbage collected, but you can also do manual memory allocation, although standard library support for that is rather lacking. Unlike Go, It also has a package manager called DUB.

http://dlang.org/overview.html

https://www.quora.com/Which-language-has-the-brightest-futur...

I'm playing with it using LDC2 (Clang), not the standard DMD compiler. LDC2 generates smaller binaries from what I saw.


> Enums. My god, Enums.

Oh yes, please.


A lot of this comment resonates with my experience and views :)

> You'll note that i didn't list Generics. I know that's high on peoples list, but not mine

This is me too.

Been programming in Rust for 3 years, and picked up Go two years ago. I like the language; I like how it feels like "C but safety net". I haven't used it for anything important (course projects a bit), but this is because so far Rust works for almost all my needs and for everything else I use Python. But I'd be happy to use Go if someone asked me to or there was a reason why Python wasn't suitable.

However, generics aren't what I want in Go. I get that a language without generics can get complex and perhaps slow to compile and has other issues too. On the other hand, enums are on the top of the list for me. Especially for the message-passing programs I tend to write in Go. There have been times when I've hacked together an enum system using interfaces and hidden methods but its not great. I've grown to get used to error handling, but I do think it can be improved a lot. Package management was a major gripe of mine but it seems like y'all are fixing that :D

I have not written production software in Go so panic-proof channels haven't been on my radar but yeah, that one makes sense.

> didn't want to make a choice that would cause them to spend weeks/months feeling unproductive.

+1 I have often recommended Go to people who don't have time but want to learn something new. If you want to actually spend time writing software from scratch in week 1 of learning the language, Go is amazing. I recommend Rust often too, but I usually find out how much time they have and/or their background first.

> Rust gives me a peace of mind that Go never came close to.

Again, big +1. For me it is two effects -- one is that Rust feels safer, and the other is that as a Rustacean Go feels wasteful at times. After programming in Rust for that long, losing performance at any corner for no reason irks me. In Rust, for example, most folks will avoid reference counting and trait objects and heap allocation as much as possible. If you see an unnecessary trait object it _feels wrong_. This is a perfectly sensible attitude to have in Rust. For me, it often carries on into Go. But Go loves interfaces and has garbage collection (with good escape analysis, but a GC nevertheless). Every time I use an interface object in Go, it _feels wrong_. It shouldn't. And I've learned to ignore it -- if I'm writing an application in Go; perf probably didn't matter enough for this to matter. (In fact, the odd unnecessary trait object in a typical Rust lib is usually no big deal either.). But, that nagging feeling is still there :)


You can also build enums by building a struct with a `Tag` field. This works well and generally has better performance characteristics than using interfaces.


That approach has its own problems, such as wasting memory (need to store any data for each tag value separately, rather than benefiting from overlapping storage ala Rust enums or C tagged unions), as well as losing type safety: one has to manually remember which (groups of) fields correspond to which tag values (although the Go loss of type safety is far better/more controlled than the one for C tagged unions). Using interfaces has neither of these problems.


Note that using interfaces doesn't get you all the type safety either, since there's no exhaustive matching. But it's good enough (aside from the possible perf issues) for most use cases.


> such as wasting memory

Granted, but I'll happily trade a few bytes of stack for zero allocs in many cases.

> rather than benefiting from overlapping storage ala Rust enums or C tagged unions

Of course, but we don't have those in Go, do we?

> as well as losing type safety

Tagged structs are not meaningfully less type safe than interfaces.

> one has to manually remember which (groups of) fields correspond to which tag values

Use an enum for the tag: https://play.golang.org/p/LI2Bh231W3


> Tagged structs are not meaningfully less type safe than interfaces.

Of course they are. What if you read from or write to the wrong field?

> Use an enum for the tag

Your example has one contained type per variant. In Rust it is common to have an enum like

    enum Foo {
      Variant1(String, u8, Vec<u8>),
      Variant2(u8),
      Variant3(String),
      Variant4(f32),
      Variant6(f32)
      Variant5(bool, bool, String)
   }

Naming tags for something like this would be hard. Some of the fields would be shared between variants. Some would not.


> Of course they are. What if you read from or write to the wrong field?

This falls under the rubric of "not meaningfully less type safe". This data structure is central in a large project of mine, and I have maybe 3 places where I switch on the type flag. I'm not proposing this as a general purpose replacement for interfaces, only a useful way to abstract over a few known types when you can't afford all of the allocs.

> Your example has one contained type per variant. In Rust it is common to have an enum like enum Foo { Variant1(String, u8, Vec<u8>), Variant2(u8), Variant3(String), Variant4(f32), Variant6(f32) Variant5(bool, bool, String) }

I agree. I don't propose this as a general purpose replacement for Rust's enums.


> I agree. I don't propose this as a general purpose replacement for Rust's enums.

Yeah, that's the thing, Rust enums used this way are very powerful. I'm fine with making tagged structy things in cases like the one mentioned, I feel Go can handle that. I'm missing out on all the useful stuff I can do with proper algebraic datatypes.


That only works well if each "variant" holds the same kind of thing. If not, you have to store them one after the other (space wastage), or use interfaces to store them in the same place (tag isn't necessary anymore, extra boxing).

Rust enums (ADTs) aren't like Java enums where each variant contains the same kind of data.


I understand. I don't mind wasting a few bytes of stack for zero allocs.


Registration is open for Startup School 2019. Classes start July 22nd.

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

Search: