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 ?
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.
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.
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...
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.
Perhaps this is changing, but 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.
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.
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.
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.
Yet it is there in the language. Why is that? how do you justify adding this to a statically typed language?
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.
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.
While I miss them certainly, I strongly prefer them being absent to a bad and complex/confusing implementation.
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 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
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".
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.
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.
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.
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.
How would you describe RTTI?
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.
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.
If you just use testing.T you will most likely end up with a lousy, developer-hostile test suite. (Also, the default coverage metrics are very very weak. But I love Go just the same.)
I 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.
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.
SQL is already much higher level than assembly. Piling on more abstraction just increases the likelihood that the code is doing extraneous work.
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.
https://github.com/jmoiron/sqlx
It has features like directly unpacking the results of a query into a structure. Very handy.
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.
I realize this is a very general and vague question but it's something I've been pondering about.
Initially, I would use `\ef <func_name>` in psql and just write and try my function, but this turned out to be not as pleasant as writing code in an editor in a dedicated file. The annoying thing with pg functions is that, if you want to replace them, you have to provide exactly the same in and out parameters. This means that if you want to change the parameters, you have to drop the function and create a new one.
This has been annoying at first, but now I'm more comfortable with it. I write my functions as migration files [1] and just migrate/rollback to do my tests. I guess I started to give more attention to the input and output parameters from the start in the mean time.
The cases where I won't use functions is when I want to do something extremely simple, like `SELECT email FROM users WHERE id = 1`. It's just not worth writing a function for that. It doesn't happen often that this is all I need to process my request either.
[1] I use https://github.com/mattes/migrate
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.
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.
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 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.
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.
Might be interesting to hear reasons - by both you and the parent commenter.
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.
The problem with interface{} is that it makes it pretty much worthless to have a type system at all.
As for saying "they are the same once the code is running", well, duh, of course, but it's completely useless by then.
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.
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!
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.
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.
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.
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! 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.
Such as?
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.
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, 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 great though, especially Elixir, but for some use cases Go is better.
Point is that they all have their strengths, but one is not necessarily better than the other. As with all things digital, it depends.
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.
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.
Also you don't need explicit yield on task runtimes like HPX.
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.
Goroutines are coroutines.
On the async/await model there are two approaches.
1 - The compiler takes care of the scheduling of the coroutines among the threads on the task pool, like C# 5.0 introduced.
2 - The compiler maps async/await into intrisics that know magic functions on the handles that represent tasks managed by the thread pool. This is called generalized async/await and is how C++17 will do it, and also how the C# model will evolve on C# 7.0
So while one must restrict ourselves with the co-routine scheduler provided by the Go runtime and to the yield points managed by it, with generalized async/await it is possible to have control how the scheduling actually takes place among co-routines.
Granted Go's model is easier to understand and might be enough for many people, but it isn't a solution for all types of concurrency problems.
Traditionally, "coroutine" has been used to refer to a userspace concurrency unit that is not multiplexed and which requires explicit yielding. Mostly I'm grumpy about broadening a previously precise definition.
> So while one must restrict ourselves with the co-routine scheduler provided by the Go runtime and to the yield points managed by it, with generalized async/await it is possible to have control how the scheduling actually takes place among co-routines. Granted Go's model is easier to understand and might be enough for many people, but it isn't a solution for all types of concurrency problems.
Not having to control your own scheduling is the selling point of Go's concurrency model. Besides, Go does offer (at least some) control over scheduling via the runtime library. I've never had to use this in practice, and I can't imagine a scenario that would require nuanced control. I don't think async/await are the worst things, but I still prefer Go's runtime at least for the problems I have to solve.
Or for that matter, coroutines on Active Oberon, which are just multiplexed as in Go.
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
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.