JetBrains WebStorm with the free Go plug-in provides an excellent Go IDE experience. There are some free community editions of JetBrains IDEs that are supposed to work with the Go plug-in (e.g. PyCharm, if I’m not wrong), so you do not even have to pay anything.
It is not excellent.
All other editors I used autoformat Go code after save.
There are two types of format provided by IDEA - IDE Format & go fmt. Both not ideal. IDE Format does not format code according go guidelines. It can delete unused imports if you go through options.
go fmt does not delete unused imports, and as result code is not compileble.
Refactorings are missing in IDEA.
It is not excellent, it is something you can work with.
-) A proper package manager. (no, go get is not enough. They have realized this and have a team coming up with a draft for an official one)
-) Well written, well maintained libraries that do proper releases. There are very few of those out there. Might get better with an offical package manager and ideally package repository a la npm/crates.io
-) Generics. This will help the previous point also. Often it's hard to write proper reusable libraries due to the lack of generics. And no, they didn't keep generics out because they are to complicated for the language users. They just (quote) "didn't find a good way to implement them", and big language changes like that are out of scope before 2.0.
-) Macros probably won't ever come, but if not, please at least give us a language construct that reduces error handling boilerplate. (Rust has some nice macros for that).
I was concerned by this at first as well, but after using Go full time for the past two years, I've never found a need/use for them. Go doesn't lean towards strict OO principles, where I feel that most of the Generics uses are applied (Arrays, sorting, etc.).
You only have "generic" functionality for built in types though. Arrays. (In a certain way maps and channels).
That's fine if you only use those, but it makes it impossible to write generic functions for much else. See my comment about lack of libraries. I did miss them a lot.
Not when writing your one off app or program, but for libraries.
And you end up having to implement a lot yourself that's for free in other languages.
For me, using LiteIDE and Atom + go-plus was what got me comfortable with Go. Without it, I felt it was far too finicky and had lots of hidden gotchas (like no unused variables).
Atom has surprisingly consistent and useful support for go - fmt on save, debugging, automatically manages imports in a file, and the autocomplete works very well.
As a habit it is bad, but this is actually standard for short bios. It allows it to be dropped in place directly for conferences, PR shots, news articles, etc without any rewrite.
Great to make it look like someone took the time to do a bio about you because you're important, while it's just you writing. Lack of self-confidence if you ask me.
How do you figure? Every time I've been asked for a bio for a conference or similar, they've asked for it in third person. Reading bios in conference proceeding in first person would just come across as weird to me.
The difference is having a bio on a website about yourself versus a bio on a website about a lot of people. No one thinks that your johndoe.com site was made by your official fan club.
You really named your user account after WHAM!? That's fucking awesome. Tell me you're either George Michael or Andrew Ridgey in deep disguise... please please.
Maybe it looks like that to you because you're unfamiliar with this format? To me it looks like he's written a short bio that's ready for dropping into where it's needed.
The default golang flag package (https://golang.org/pkg/flag/) does not implement gnu-style long-with-shortcut options. Instead, one and two-dash arguments are identical. For example, -enable-pants and --enable-pants are equivalent. Compare this with the GNU style where you can have -p or --enable-pants.
Most go programs I've read use spf13's flag library, which is GNU-compatible.
Is this a remnant of Plan9 or something? Ugh, this is why I don't dabble in ecosystems with overly opinionated leaders. You have to know when to have a strong opinion and when to do things the standard way, and some people can't do that.
I guess the claim is that any more expressiveness would necessitate losses in readability or simplicity that the expressiveness doesn't make up for.
Personally, I think you're sense of relative value of those has to be pretty unusual to thinks it's "perfect". Though valuing simplicity above almost everything else is what's written on the tin with Go.
No, it's verbose and recommends bad and repetitive code style which affects both readability and simplicity. The only people who'll find this language "simple" are those who never understood any more "complexities" than functions and loops:
"
The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt.
– Rob Pike
"
It is pretty readable, but really non-functional too. Eg, they still don't have math.round and you wind up writing a lot of code because it can be a little bare bones or does things in a way that isn't quit usable.
I wouldn't say it is very expressive either. Go can be very wordy and verbose at times, and some things are downright a pain or poorly thought out (e.g, while Java has finally and C++/Rust have RIAA for dealing with resource deallocation Go has defer and it doesn't work really well when you encounter an error in the dealloc logic).
Simplicity is about Java 1.0. Give Go some time to add some technical debt before you get too excited about it simplicity.
It isn't a bad language. This is my second run thought the language, and I'm neither excited nor turned off by it.
I think he meant non-functional in the sense of "not functioning", not functional programming. Neither of his immediately following complaints have anything to do with functional programming.
No, it isn't since go lacks generics and dynamic types - which helps to reduce boilerplate(even if the latter isn't that effective).
> Of course it's not as clean as Python, it's statically typed.
There are cleaner statically typed languages then python - look at fp languages with type inference(ML dialect)
> But type inference makes code way cleaner than your typical Java code (Javas type inference is improving, though).
The typical java code is legacy, but for a fair comparison you should check out projectlombok [1]. Otherwise, Java is on the JVM which means there are alternatives like Scala/Kotlin etc.
* Interfaces which can have fields would be really, really great.
* A rust-style borrow checker would make dealing with channels and pointers a lot safer.
* Equality operations for slices would clean up some ugly parts of the standard net library (addresses are []byte, so a direct x == y doesn't work but seems like it should).
* Closed enum sets: some ability to setup an enum type which can be "complete" when used in a switch statement without needing a "default" and which will only panic/fail when a bad type conversion is made. Let me sanitize out a whole bunch of possible errors at one point in my code when I want to use constants (and be warned when I break the guarantees).
My two Golang pet peeves, not very important either:
-still no math.round WTF?
-make() should be syntax and not this quasi-function that takes this vast array of argument types, and while you're at it, you should just rename it `new`, because that's what it is.
Yes, and make() is completely superfluous. make() and new() can be completely rolled into one piece of syntax without ambiguity because they are the same thing.
some legacy Windows server stuff - part of this could be rewritten to Linux/Go/whatever_server_lang just team mates are not willing to leave their comfortable VS env. if not provided with decent IDE/debugger
Interesting. I've used C# heavily at my last couple of jobs and it has all been server side web work - mostly back ends of web apps, but also services and microservices of various kinds.
From talks with coworkers, the impression I've gotten is that most C# in the wild is running server side, though there's obviously a lot of it in desktop apps, and on mobile with Xamarin.
Can confirm. At work we're moving from Clojure 8 to Java 8 partially because IntelliJ is amazing and partially because Java 8 plus the right libraries is close enough to Clojure anyway, except much faster.
I think as developers we are so used to pain that we forget how "easy" development can be if we allow people the right tools.
If any language had the tooling and development Java did it would hold the market share. It's my opinion that if Rust makes a similar IDE experience (even just using Eclipse's platform) with every feature like code completion, formatting, debugging, the hole 9 yards and integrate Cargo into it they'll win. But, here's the caveat: It needs to be FLAWLESS integration.
I've been a java developer for my entire programming career. Java was my first language when I was 12 and I'm 19 now. I still can't use javac without looking at a compiler guide since I never use it. Eclipse does the business and I don't ever even need to think about what's going on outside my code. Not once.
That you need an IDE to make a language bearable speaks volumes to the design of the language.
Go is doing well because it realizes we can strip so much away, stay with the standard lib, and get real work done quickly.
That's not to say that Java, in particular the JVM, isn't an impressive piece of engineering, but more that we should be careful when we choose our tools so as to not overly complicate things.
While Java may be somewhat verbose, good tooling is not just about "making the language bearable".
Things like being able to jump to a definition, rename it throughout the project correctly and jump back without even thinking about it are hugely valuable in any language.
And maybe you have ways of doing that in your workflow too, in which case great, you too appreciate the value of good tooling.
Rather than looking at it as a language that needs an IDE to be bearable, it might be more helpful to look at it as a language that is most pleasant to use in concert with well build tools.
I look at it similarly to the way I look at Smalltalk. It's possible to use it as just a series of text files (with GNU Smalltalk), but using it with tools that understand the language (such as Visualworks or Squeak/Pharo) turns it into a very different experience.
I know that a Smalltalk dev environment feels alive in a way that Java doesn't, but using Java with a tool like IntelliJ is a nice experience. Using something like JRebel or HotSwapAgent to swap code changes into a running application also improves the dev experience significantly.
I'm not a Java developer, so I'm interested to know what the amazing parts of IntelliJ are. When I look at it it just looks like they ripped off the skin from Sublime Text and it has some debugging features that are readily available in other editors. I get pretty much everything on https://www.jetbrains.com/idea/whatsnew/ in my vim setup.
I didn't say features, I said skin. Of course IntelliJ has been around longer.
If you like an IDE that's cool, no hate. I just get frustrated with the idea that I need one to be productive in certain languages. Give me Go and vim, maybe inside tmux, and I'll be productive immediately.
whoa!!
this is huge - can you talk a bit more about your stack please. This is the first time I have heard something like this - what libraries, code style, etc would get you the same "feel" as clojure ?
are there any opensource github projects that illustrate the use of this infrastructure (java 8 + lombok + streams + lambda) in a very clojure-ish way ?
its hard to find good examples of this style of coding. For example, it is hard to figure out which app server (Spring Boot, undertow, dropwizard, wildfly). I'm sure you can use any... but it would be nice to learn from your experiences.
Could you provide an example project that you'd like to see implemented and I'll give it a go? I'm not the person who's doing this but I think I'd like to mess around with functional programming and I love Java.
This is an example of a project that is not UI centric, has quite a bit of database integrations (postgres), mailer integrations (mailgun), scheduler (https://dzone.com/articles/distributed-tasks-execution-and-s...). It has also has a bit of math to compute stuff.
Even without a UI - it is something that could be very useful!
Nope. It is absolutely false. You will always need a debugger and there are some problems that are not unit testable at all.
How do you unit test a random WPF bug?
And you'll always need a fancy code editor unless you're masochist.
If WPF was designed from the get-go using TDD, there would be less of a problem here in this case.
I myself have hardly ever needed a debugger in any code i've worked on that has been TDD from the ground up. And it is almost ALWAYS related to OTHER code that was clearly not written with TDD in mind.
Tell me something: What exactly is a "bug"? It is literally just a state the programmer did not expect, correct? So how do you keep all states expected? By constraining them (types, guards, etc.), by testing them in the small (unit testing, TDD), and by making them (ideally) immutable values (preventing unexpected changes to values within one stack frame). If you can control all possible states in the small, you can largely eliminate bugs in the large.
At least, I've found this to be the case after 20 years of working on procedural and OO code...
"This approach removes the GUI layer from TDD and unit testing. It does not mean the GUI is never tested but just acknowledges that it is not cost effective to pursue automated GUI testing, particularly as part of TDD. Integration and user testing should cover the GUI."
That is true, I used to work in legacy Java code base and the debugger was my Lord and Saviour.
I am now working in fresh, well tested (both unit and integration) Go codebase and I yet to have to use debugger. Usually adding few debug logs (which usually just stay there for future) and writing test case for new discovered edge case is enough.
Programming in C# feels almost like scripting. Blazing-fast compilation with the added benefit of static type system. If only MS took a different path of cross-platform support in the beginning.
>As I had before, I recognized that this technology had the potential to revolutionize how software was written at a fundamental level.
Congrats and all, and I love Hugo (use it all the time).
That said, I think the kool-aid was spiked with something.
Sorry, but in what way is Go "revolutionizing"?
I'd buy "refreshing simplicity" and "yay, static builds again" but it's hardly revolutionary, even in its most touted feature (channels).
And it hardly changed how people write software. At best, it introduced some dynamic language programmers to static types and static builds.
All the rest, including creating a language that's easy for average programmers to program at scale was already the goal, and a success at that, of Java.
Some of the harder more time consuming things in high level programming include abstraction/polymorphism, concurrency and dependency management which are all simpler and clearer in Go than in Java. Think about how many hours you have to invest to learn any of these things in either language.
> abstraction/polymorphism, concurrency and dependency management which are all simpler and clearer in Go than in Java
Really? A lot of copy/pasted code, generics replaced with casts on empty interfaces, poor support for collections, only very primitive concurrency tools, and so on...
I can understand that simplicity is good for small CLI/network tools, but it is hardly an alternative for really big applications where Java is vastly used
What I'm saying is Golang is a recent programming language so it works best when used for multi tier/services, not to create a giant monolithic Java app that has a huge number of loc.
What does Java have to do with big monolithic apps? I think you're confusing complex application domains with bloat.
Go is supposed to be a systems programming language. That means a very, very wide range of possible domains, where for many of them even network latency is unacceptable. For example, Photoshop would have a terrible user experience if it wasn't the monolith it is. A game engine wouldn't go above ~20FPS (even 60FPS games have a network loop that is capped around 20FPS).
If I can't do systems-level programming in Go, what's its advantage over real systems languages or higher-level languages then? Especially when you consider what Go offers (CSP) comes in opt-in libraries in many other languages.
I feel like Go should be put in the same category as C# and Java. They're just above systems languages but just below scripting languages; giving you the worst of both worlds. Not bare-metal, but also not that productive.
Think of all the hours you save not having to work around the misfeatures available out of the box in other languages. I know, you don't make mistakes or use those nasty features, but think of your coworkers.
Of course you can. None of those languages would be in use, if projects could not be managed at all. The question is: are you benefiting from their "features" or are you able to succeed despite of the challenges brought up to you because of those features and their complexity?
It was the simplicity of Java 1.0 that lead to some of the famous patterns we see in some frameworks, that were initially created to workaround language limitations.
The same path that is being followed by those that rely on libraries to reduce error handling boilerplate and go generate, just as two examples.
Who is using libraries to reduce error handling boilerplate? I rarely see `go generate` used for things that don't require static code gen (e.g., automatically generating bindings to C libraries). Sounds like you're inventing problems...
Go's big success, in my opinion, was to fuse the lightweight, spawn a million threads we don't care, style of programming to conventional imperative, conventional static-type programming. I know of several other runtimes that had that lightweight threading capability, I've been using them since years before Go was even a thing, but they all required programmers to learn a new paradigm. At times, a radically new paradigm (Haskell).
You may complain that programmers have a professional obligation to learn those new paradigms. You may complain that programmers should consider "learning a new paradigm" a smaller strike against a language, especially if it brings other good things with it. But that won't change observed reality, in which programmers do want to not have to risk several months to a year on something they aren't sure will pay off. By contrast, if you know any of the existing manifestly-typed languages at all (Java, C, C++), which most programmers do to some extent, you'll be writing fancy concurrent code in Go next week. Possibly even "tomorrow". You can be shipping production code in a month.
The language nerd in me really wishes Go had isolated its goroutines from each other and only passed things by value. (While I like Rust's much finer-grained handling of that for a lot of other reasons, you can get a lot of the benefits in a language where the threads aren't supposed to be that large by isolating to small threads, and it's way easier to implement.) The pragmatist in me is forced to reluctantly admit that had it done so it probably wouldn't have succeeded as much, because while that may not have been a "full" paradigm shift if the rest of the language was still imperative, it still would have been at least half of one. That affects how you design larger programs. In a lot of good ways, I think, but still, change with an unknown prospect of payoff.
So... it depends on your point of view. It's not revolutionary in the sense that there's anything new in it. But it is a somewhat revolutionary combination of concepts.
You're even free to join me in being a bit sad that fusing your concepts to an Algol-descended language is basically mandatory for success right now. But, one step at a time. We are slowly creeping away from it. As I've often observed, programming as a profession does not move anywhere near as quickly as some people think; there's a constant surface churn of technologies and languages, but deep paradigm shifts take decades.
I take no issue with your take on the language, but the OPs' opposing views on "revolutionizing how we write software" isn't really addressed by you.
Go is popular because it is accessible. The cognitize load of the language is in effect ammortized by being spread over the life cycle of the dev process (i.e. run vet to do x) instead at localized in cycle/space of code-time/source-code, so even professionals not bothered by complexity can see an incentive to using the language.
But I am not aware of the language enabling a class of software systems, etc., that have become more feasable because of the language.
As has been pointed out by many, at some point even the novice programmer has to get serious given the mix of pointers and channels and sync constructs which demands a degree of sophistication, so the accessibility aspect degrades over time.
And then -- and here is why I feel there is pushback regarding Go koolaid -- this somewhat on 'the fence' approach of the language towards concurrency (originally "communicate don't share memory") gives this impression of a glorious hack but a hack aspect to this "revolutionary mix" that you refer to. At some point, the ala cart type system of Go presents issues to people who have invested the time to get serious about these matters but find that their favorite light-weight language is not fully up to the task of keeping up with our conceptual models of thinking about our software.
> I know of several other runtimes that had that lightweight threading capability, I've been using them since years before Go was even a thing, but they all required programmers to learn a new paradigm.
One of those runtimes is Ruby (Ruby uses lightweight threads, not OS threads), and Ruby is not a new paradigm.
Ruby (that is, the main implementation, from 1.9 on) uses OS threads with a global lock so that only one thread running Ruby code is executing at a time in a process, same as, e.g., Python.
Up through 1.8.x, Ruby used N:1 lightweight threads (multiple lightweight threads on one OS threads.)
Go uses M:N threading (multiple lightweight threads, which may be distributed among 1 or more OS threads.)
M:N threading is a different model than either the Ruby 1.9+ model (1:1 threading on OS threads with a global lock) or the older Ruby approach (N:1 threading), or even the JRuby approach (1:1 threading on OS threads with no global lock.)
Was it stable enough to really use, though? Python also had gevent and stackless, but as far as I can tell, it never really got to the point where you could just count on it in production at high loads. It was never as stable as Erlang. They were more on the toy side that production-reliable side.
For further proof of my point, I submit the fact that you are basically correct, yet those technologies never took off, even in their own communities. The dominant way all the scripting languages are written are as single threaded code, and the second most popular way is as event-based code. It's not easy to bolt that stuff on ten years after the fact, which is what most of those technologies did.
Lua would probably be the better thing to cite as the possible counterexample, in that it does seem like a lot of the people using it are at least using coroutines, though I believe it is not possible/feasible to get them threading in any nontrivial way.
(I'm not taking the out of claiming dynamic typing counts as a "new paradigm"... probably the single largest true programming change in the last 20 years is the mainstreaming of dynamically-typed programming languages. Which I think was still a good thing for static languages because it raised the competitive bar for them; I've personally swung back over to static typing, but the easy of the dynamic world has pushed the static world to greatly improve its cost/benefit proposition, in both categories. If my only choice was dynamic typing or what we had in the 1990s, I'd still be in the dynamic world.)
As a final postscript, may I observe that while I think that Go was a modestly revolutionary combination of concepts, I consider it an indictment of the programming community that it did not happen sooner. I don't see Go as if it somehow won a big race by being the best... I see it as a situation where it's the first thing that bothered to show up, years late, somewhat desultorily.
By offering simplicity. Taking a step back to before C++. And providing a better C. C + strings + GC + map + slice(array) + json/xml parsing + http transport layer (server & client). What else do you need? Ideal for minimalists. That's its beauty. Programs are very stable. Takes less memory than Java (some amount more than C/C++). Faster compilation time compared to C++. A convenient C. For minimalists, who like to cut down all the jargon, and solve the problem. Looking at it that way, you see its beauty.
I keep seeing this come up and I wonder if people have actually used C in the domains where it matters.
Go is not a C replacement/better C. You don't have control over what goes on the stack/heap which means you can't control memory layout. If you can't do that you're throwing away one of the key reasons you'd use C. Memory layout is critical to getting that 10-50x performance that you can't get in a managed language.
There's a bunch of domains where Go excels but if you're on constrained hardware where you really need the last bit of perf/memory it would not be the tool I reach for.
Its not a replacement for C in most of the places where C is far and away the best existing choice for a whole project, it is a replacement for C in the zone where C's performance and static typing are attractive and would decided the choice in its favor, but Python's expressiveness, batteries-included stdlib, etc. also make it attractive and make it a little bit painful that the other factors weigh in C's favor. For a system that you might be otherwise do polyglot Python (or some similar dynamic language)/C development for that reason (or where you might do C-only somewhat reluctantly for that reason), Go could be a reasonable replacement for C (and, in the case where the alternative would be polyglot, Python or whatever else.)
Sure, but in those cases Java/Swift/C#/etc are also reasonable replacements. If you're not taking advantage of data locality then it's possible to approach C's performance in any of those languages.
If your primary concern is performance(which is the only reason I'd reach for C today, esp w/ security concerns) then Go is not going to replace C in that domain.
> Sure, but in those cases Java/Swift/C#/etc are also reasonable replacements.
Well, Swift certainly wasn't when Go was introduced, but, yes, Java and C# were competitors in that role when Go was introduced (Java, even moreso at the time, tended to fall short on expressiveness -- which some argue Go does too, though in different ways -- and C# was deeply tied to .NET, which only had a first-class implementation on Windows; both of those have improved somewhat since Go was introduced.)
Swift probably, but Java and C# still suffer in a lot of situations from the lack of good AOT compilation options. There are implementations for both, but they're incredibly primitive compared to the JIT reference implementations.
Unfortunately .NET Native is still for UWP only, so for most places you'd use Go it's pretty useless. Microsoft appears to have no plans to even bring it to ASP.NET: https://github.com/aspnet/Home/issues/285
Is the .NET stack and tooling any more a language feature? You were happy to argue that tooling was important a moment ago.
In any case, when choosing a language for a project which doesn't fit your requirements because maybe the tooling will get there someday, I think you're going to pretty consistently end up in a sticky situation. I like C# a lot more than Go for most things, and some of the reasons are things you listed, but the goalpost shifting is getting ridiculous.
> You don't have control over what goes on the stack/heap
You do have this control. There is escape analysis, but I've never had to dance with it to make it put things on the stack when I need them to. I can get very close to C speeds with Go without much effort.
Can I say X is a value-type(or better annotate it in declaration)? If not then I don't have that control.
Your compiler doesn't know your data access patterns, dataset size or a myriad of other factors that you need to tune if you really want to get full utilization from your hardware. To be clear I'm talking about ~5% of software out there, but when you need it you really need it.
Yes, the distinction between value and pointer types is explicit in the language, so you have all the control. This is, why you also have more control about the memory layout than in Java, which makes a huge difference in performance.
There are lots of cases in which escape analysis falls down. In particular, higher order control flow (closures/interfaces) will always cause it to fail, until Go implements some sort of higher order control flow analysis (which I doubt it will). Go uses interfaces a lot.
C# doesn't have escape analysis, but it has stack-allocated value types. Java doesn't have stack-allocated value types (yet), but it has escape analysis. It seems a lot harder to me to reason about escape analysis without value types, and I have no idea what happens in C# if you take a reference to a value type (i.e., is it moved to the heap?).
I never meant to imply escape analysis was somehow perfect; only that it is (mostly) easy to reason about.
If the constrained hardware is more powerful than a Xerox PARC Dorado (Mesa/Cedar), a ETHZ Ceres Workstation (Oberon), DEC Firefly (Modula-3), then Go is certainly a better C.
Nothing prevents you to make use of statically allocated data structures, hands off from the GC if really needed.
In spite of lacking a few features that I consider any modern language should have, ability to create memory friendly data structures isn't one them.
> Nothing prevents you to make use of statically allocated data structures, hands off from the GC if really needed.
Only if you give up closures, interfaces, defer, strings, the entire standard library, and all sorts of other things that don't immediately come to mind.
Programming without the GC in Go is technically possible, but impractical. The language just wasn't designed for that (and this isn't a knock against the language--it just was a non-goal).
I didn't say to not use the GC at all, rather to use it less.
We can use the GC for the 90% of the code that isn't performance critical, enjoying the programming productivity of using a GC enabled language.
After profiling the application and find out where those 10% performance/memory critical hotspots are, re-architect the data structures to use static allocation, either via the stack (compiling with escape analysis validation) or the global memory. Or even doing off-GC Heap allocation via syscalls.
Then make use of pointers or array indexes to access the static allocated data.
Plenty of examples in Modula-3, Oberon, Mesa/Cedar research papers.
There are plenty of languages that I would rather use for systems programming instead of Go, but the language is certainly capable of being used in the same scenarios as Oberon.
For example Astrobe, which sells bare metal Oberon compilers for ARM Cortex-M4, Cortex-M3 and Xilinx FPGA Systems.
If anything, comparing Oberon's SYSTEM with Go's unsafe packages, Go could use a few more intrinsics like CPU register access, but those as also doable with a bit of Assembly help. Which ANSI C also doesn't support.
My goal is just creating awareness that alternatives are possible, it doesn't need to be always C.
Just to make myself clear, on the point of 'providing a better C'. I just made that comment from language evolution point of view. And did not at all mean that Go is better than C, in all respects. I dare not say that.
What I meant was taking a step back to a point before C++ was created. And looking ahead, and choosing what works and is essential (strings, GC,... and some modern Internet usage things...concurrency...etc), and getting rid of all the bloat, which actually comes in the way, especially the opinionated bloat on how software should be done e.g. OO. And making a language based on that.
For some reason, this discussion reminds me of Linus Torvald's angry defense of using C, for git[1] :-)
How do you know this isn't just a new language thing ? All of the advantages you say always result from essentially any redesign, and they never last.
A quote from "Java in a Nutshell", from (I believe, 2002):
"Because Java is a simple and elegant language with a well-designed, intuitive set of APIs, programmers write better code with fewer bugs than for other platforms, again reducing development time."
Reads pretty similar to your argument for Go. I remember similar quotes for Modula-2, ML, (a lot of versions of) LISP, C itself, and I'm sure there's many more. It seems to me pretty likely that we're seeing the same evolution : Go currently is a simple, easy language that matches the current hotness (microservices) pretty well, because it was directly written to support that. The current hotness will go away and Go will have effectively-mandatory bloated libraries to support the next hotness, just like today it's becoming close to embarassing to use Go's standard library's http server for anything non-trivial.
Additionally, many of the criticisms of C apply to Go. It's looooooooooooooooooong simple code doing "a; if error { ... }; b; if error { ... }; c; if error { ... } ...". Many abstractions just don't work, and have to be "fixed" with crutches (like sorting, logging, ...)
Aah, Seeing such a good reply, so late. Does HN need notification? Probably not :-)
So, I agree with you a lot. There are no guarantees that Go will remain good for ever. It would be a challenge and need effort on the part of the language developers.
That said, I believe, when a language evolves over a period of 20 years. You get the benefit of hindsight to introduce the good things in a new language. So, Java's garbage collection is a big success. And Go developers incorporated it. Its a tradeoff with extreme (C like) performance, and devs went with convenience and security. Good call, in my opinion.
But the object oriented part of Java, in my opinion, is just a C++ inheritance, as at that point of time (mid 1990s). OO was the silver bullet, to all your sofware spaghetti problems. But as it turns out, it got more complex, if the design/architecture was bad. And good engineers design good enough without the OO.
So Go has taken that approach, which gels with me and lot other developers.
Your comment regarding the error handling part. Yes, it is cumbersome. But surprisingly, it seems to look better than exception handling when you write couple of modules with it. And for some reason, which others have articulated better than I can at this time, your programs are more stable as a result.
So overall what I like about Go, is the minimalist approach of just having the proven things which are good. And avoiding which are grey area or bad.
>By offering simplicity. Taking a step back to before C++. And providing a better C. C + strings + GC + map + slice(array) + json/xml parsing + http transport layer (server & client). What else do you need?
Language consistency (no magical functions like "make" or magical items like slices), DRYness (generics), better type-safety than C (interface{} is the void * of the 21st century), would be a good start.
Due to the mandatory GC, it's also not a C replacement where C is good at. Hence the backtrack on "systems", etc.
After posting, I realized its concurrent programming model, is another thing which I (and many others) am a fan of. I found the channel and go based approach, much better compared to explicit spawning of threads in languages like Java and C++.
> Due to the mandatory GC, it's also not a C replacement where C is good at. Hence the backtrack on "systems", etc.
I tend to complain on Go's design decisions and don't have any actual use for it, but given my experience with Oberon, I do believe there are use cases where it would be possible to use Go as systems language.
For sure not in when targeting 8 or 16 bit CPUs with a few KBs, or timing constraints that cannot deal with possible GC pauses.
But for more beefy micro-computers like the original PI, it can certainly be an option.
This would a good starting point for anyone trying to attempt it
>At best, it introduced some dynamic language programmers to static types and static builds.
Can confirm (well, add another point of anecdote), I played with it a bit in order to get some experience with static stuff before diving into the whole C ecosystem.
Next thing I build that needs that kind of development will probably be C though.
I'm sorry to hear that. Go is not revolutionary (unless you count "completely ignoring runtime errors" to be revolutionary). If anything, functional languages are the new, old, revolutionary thing.
However it treats them as product values (tuples) instead of sum values (Either), so it's perfectly possible to ignore the error and take the result anyway.
Ignoring on purpose is fine. The problem is when the only return value is an error which can be ignored simply by not assigning the result to a variable. Forcing the user to do:
_ = f.Close()
... in order to explicitly ignore the error would be nicer, but then, there are many functions which are known to never be checked for their errors, like fmt.Println. That's why the typical advice is to use errcheck, which has filters to avoid "false"-positives (I am not convinced that some function never should be checked for errors).
> functions which are known to never be checked for their errors, like fmt.Println
In Java println is checked for errors, as it should be. Every Java program that does println either aborts or deals with the error in some way.
It matters. Take any program that writes to stdout and run it with ">&-" to close stdout. Because of unix, the next file it opens will be file descriptor 1 and the program will start writing the console output to the file. You want the program to abort instead of trashing a file, and especially not to trash /etc/passwd.
That's why you don't want to ignore errors, because any time you do there can be unintended and hard to imagine consequences -- even something as simple as writing to the console. Simply put, Go encourages buggy programs with poor error handling.
In unix when you open a file it gets the lowest available file descriptor number. So if you run a program with no stdout (file descriptor 1) then it gets a "bad file descriptor" error anytime it prints something until a file is opened, gets file descriptor 1, then it starts writing to the file.
# ./a.out
first message
second message
# ./a.out 1>&-
# cat output
second message
By ignoring the error, the program continued on and then wrote console output messages to the file after it was opened. There have been exploits due to this bug, but the real point is that you could never predict this failure without good knowledge of unix and careful consideration. This is why error codes should not be cavalierly ignored, because it's really hard to know what might happen if you do.
Last I checked, Go operates the same way as this C example. Java fails on the "first message" if stdout is closed so it doesn't trash the file, not because they even specifically thought of this scenario but just because errors are not ignored by default and are not easy to ignore.
How does treating them as values force you to deal with them (except on principle)? Does the language force you to check the return value of every line of code? And the checking would only encompass expected errors, not unexpected ones, no? The unexpected ones (read: bugs) would simply be swallowed up and the actual problem would finally turn up far away in some other stack scope, no?
True, you can discard errors on purpose by assigning them to "_", which is an explicit way of saying "I do not care about this error happening". If you do that and get a panic down the line, you already know where to start debugging. That's much better than having an exception bubble up from deeeeep inside your code with some context-less cryptic message or number, isn't it?
No, you don't panic, you are most likely to corrupt your data and maybe continue working as if everything was fine.
If you are lucky, your code panics.
> ... you already know where to start debugging.
Grep all occurrences of "_"?
> That's much better than having an exception bubble up from deeeeep inside your code with some context-less cryptic message or number, isn't it?
No, the exception carries all the context you need.
If you ignored a previous exception, the next one won't be as useful as it could, but at least your code breaks loudly.
Also "_" means the programmer chose to ignore the errors that he knew about at the time.
There's no way to know whether the programmer actually considered all the error cases (they are generally very poorly documented in Go code), and there's no way to know if the called function added errors at some later date. If it's though an interface, there's no way to know that all implementations have the same errors.
It's really an 80s-era approach to error handling, and that's bad.
> That's much better than having an exception bubble up from deeeeep inside your code with some context-less cryptic message or number, isn't it?
I don't see how that follows. A stack trace (as initially cryptic as it seems) is a record of all the code that would have affected the current buggy state you find yourself in. If anything, a stack trace isn't comprehensive enough, ideally it would include all known/named values at every level of the stack... a large quantity of information, to be sure, but perhaps it could be narrowed down by diffing each stack level with the previous one or something. But if you could have those 2 things, you would have literally ALL the information you needed to fix the bug. Since bugs are programmer-unexpected states, and the stack trace plus the known values at each stack level are LITERALLY the entire state.
So correct me if I'm wrong, if you don't assign an error to a variable on the same line in Go, it throws? Because it was my understanding that there were absolutely no throws at all in Go. Or is it only that certain kinds of statements are expected to possibly error, and must therefore be assigned to a variable (or _)?
Any error return values in go must be assigned to a variable at call time, otherwise it's a compile error. Unused variables are also a compile error, so you are forced to do something with it.
Assigning to _ effectively ignores the error, but is considered a very bad practice.
Unavoidable runtime errors (eg divide by 0) will generate a panic, which is similar to an exception, but is uncatchable within the Go routine it originates in.
Yeah, that's what I thought. And I've also been there, done that. Is there basically some willful blindness around this issue with Go fans, or something? Or a lack of experience doing code? Or am I completely missing something?
To illustrate my point: If I implemented some network program which converses with a remote server in multiple steps in an language with exceptions and something fails I might get a stack trace and the message:
"Syntax error, expected token HTTP_METHOD but got 'B'"
In Go, with proper error handling I'd get a message like:
"Couldn't do flubber transfer: Unable to negotiate protocol: Sent supported protocols but remote responded with HTTP 400 "Bad Request": Missing required field 'version' in protocol definition"
As "error" is an interface, this message is only the tip of the iceberg: the error object itself may contain the address of the remote server, the protocol definitions that were sent, the response body that we got and any other relevant contextual payload.
This is all due to the error being treated in-context as opposed to just bubbling up.
Well yeah, if you handle errors properly, you get better results than if you don't.
The point is that with exceptions, it fails loudly and you get the cryptic message even if you forget, whereas with Go, if you forget to handle an error, you just get weird behaviour and corrupt data.
And if you want more helpful error messages, you can catch the exceptions and report the context just as easily as you can in Go.
> > That's much better than having an exception bubble up from deeeeep inside your code with some context-less cryptic message or number, isn't it?
> I don't see how that follows. A stack trace (as initially cryptic as it seems) is a record of all the code that would have affected the current buggy state you find yourself in.
You get stack traces in Java, C#, Python, etc. You don't get them in C++ (well, you aren't guaranteed to get them).
But you agree that a stack trace is a tool to mentally reconstruct (read: "realign") your mental model of the state the code produces, with what really happened?
Wow, that talk just demonstrates that all downsides about error handling in Go are due to its lack of exceptions (at least how they work in languages like C#). It's a step backwards, it's ludicrous.
I watched this, and what I saw was a heck of a lot of information about how to extract information from error values and percolate them together up to a handler, all of which would automatically be present in your everyday stack trace in a language that actually threw unexpected runtime errors. ;)
I frankly don't see how this is OK, nor better. Errors are real in the sense that they are evidence that the programmer's mental model of state did not line up with the actual state (usually in some corner case). There is literally NO circumstance in which being in a programmer-unknown state (yet still "alive" so to speak) is better than being in a programmer-known-and-accounted-for state. (For an example of being in a programmer-unknown yet alive state, see: Every Security Hole Ever.) This is why "throwing" is appropriate, no matter how much the Go creator didn't like it. If even a single runtime error goes undetected/unhandled, you will end up further and further into an unknown state due to state corruption, and your behavior will soon become completely nondeterministic (or at least, nondeterministic-appearing). Losing determinism is the first step on the road to dragons.
I literally can't see how this wouldn't happen, and what I AM seeing is a lot of chatter in Go communities about how to manage this problem, when the solution is already there: Just fucking throw a stack trace. Or, even better, do what Erlang/Elixir do: Throw it, log it, and have a supervisor process kill it and restart it, all in 1 millisecond or less. If the error keeps happening above a certain threshold rate, kill and restart the supervisor as well. The reason this works (and has resulted in the INSANE uptimes that Erlang-backed services have) is that it resets state to programmer-known conditions.
I don't think the original Go authors ever truly thought that exceptions are "bad" per se. I think they just took it off the table because of the complexity it added to the language, both conceptually and implementation wise. A (the?) primary main goal of Go from the beginning has always been to make implementation as obvious as possible; in other words, to make it a slightly thicker layer on top of assembly than C was (they seemed to like Java 1's idea of interfaces), while still keeping in mind that it will map to assembly. The language's design is inherently tied to its compilation model. And to add exceptions would add complexity to it which for some reason they considered to be "too much". Same story with generics fwiw.
To whom is the implementation of a generational, concurrent GC obvious? It requires a lot of compiler infrastructure and complex runtime support, much more than stack unwinding. I'm not a fan of, say C#'s, exception model, but it's far from the complexity of features Go already has in either implementation or programmer understanding.
On top of that, unwinding isn't the only way to get something where errors have to be handled or the program dies. Look at Swift's error handling for example: it's basically sugar over returned errors with correctness checks. Dead simple implementation, and dead simple to explain to users.
What I don't buy about the justifications for Go's design is that they were driven by the pure guiding hand of simplicity. It looks more to me like the designers simply treated language features they had seen done badly before as anathema and didn't try to think about how the mistakes could be corrected.
Not really. It's a top-level comment. If it were a reply to the "in what way is Go revolutionary" comment, the "sorry to hear that" could reasonably be inferred to mean "sorry, but..."
I'm joining the Go team at Google
Sorry to hear that
Go is not revolutionary...
I interviewed at Mongo a long time ago for a Drupal developer position. It was strange, they took me through a bunch of whiteboard algorithm crap that had nothing to do with Drupal. Obviously they weren't Drupal devs and didn't know how to interview for one so they just gave me the standard CS stuff. Obviously I was a Drupal dev so I didn't know shit about actual CS topics. I failed hard and learned a lot from it.
Steve was the only one of the 3 that I interviewed with that didn't come across as a self-important asshole. He was kind and helpful and overall seemed like the perfect guy for a Developer Advocate or whatever the title was. I wish him luck.
> they took me through a bunch of whiteboard algorithm crap that had nothing to do with Drupal.
Oh I hate that type of interview. I had a similar experience getting interviewed for a front-end dev position by two backend devs at a consumer VPN company in Toronto who asked me to write common CS algorithms while they both sat there staring at me.
Even ignoring the total lack of connection between the content of the algo with the job in question, I've never coded like that in my life. Largely because I also never took a CS course and didn't bothered to study cliche interview questions because I don't apply to big cos. But most importantly, I don't solve problems on the spot in front of other people IRL, so what aptitude are they testing for exactly?
I'm an introvert who always codes with headphones on and likes to spend time thinking through problems on my own - but can also work with other devs to solve problems when needed. I also had OSS projects, private repos I could show them, and live production apps with tens of thousands of visitors I built myself to point to my aptitude. That's been more than enough for other smart teams to hire me. It's also the content I look for most when I've hired people myself.
The non-technical founder also never told me ahead of time they were going to do this type of interview so I was surprised when they sat me down.
Also during a telephone interview with their other semi-technical founder asked me to describe how NAT works (yes asking a front-end dev to describe network implementation details). I figured he was just confused about what I would be doing for them... then I showed up to meet the two backend devs the next day.
I just walked out of the interview and told the founder his hiring process was silly, even though I liked the company (product). I'm happy I did.
I should note that I'm not against the idea of measuring aptitude during the hiring process, if companies want to be thorough. For example. during another interview process with a different company a dev team gave me a problem to solve (with ample warning before I arrived that day) and let me solve it on my own, using my own laptop in a quiet room, while they went back to work at their own desks for an hour or two (taking however long I needed). I was fine with that process and it worked out. There was also a collaborative element to it as well. That was a test better grounded in the reality of software development.
Person who interviewed you here. Our interview process may not be perfect, but surely going into a technical interview, you could expect some coding questions. You mentioned that we "asked [you] to write common CS algorithms", like we asked you to do invert a binary tree. That, indeed, would be ridiculous.
In fact, if I recall, we asked you to reverse a string. This is not an "algorithm" question, it's a basic programming aptitute test and if you can't do it on a piece of paper, this is a major red flag for us. Furthermore, you demonstrated a lack of understanding of basic JavaScript concepts like prototypes and scoping. This, and your personal attitude, were the reason why we did not hire you for the front end deveoper position.
You are no doubt a hardworking and resourceful individual, but we were looking for something more than ability to glue JavaScript libraries together. I wish you, and "other smart teams that hire [you]", all the best.
210 comments
[ 3.6 ms ] story [ 229 ms ] threadPersonally, golang "IDE" experience is not as good as Java or even C++.
For example, ycmd only supports subset of functions for golang. I miss ycmd-goto-references the most.
https://www.jetbrains.com/products.html?fromMenu#type=ide
Refactorings are missing in IDEA. It is not excellent, it is something you can work with.
Most go modes for various editors use native go utilities as their backend, for example gocode/godef/golint/gorename/guru, etc.
-) A proper package manager. (no, go get is not enough. They have realized this and have a team coming up with a draft for an official one)
-) Well written, well maintained libraries that do proper releases. There are very few of those out there. Might get better with an offical package manager and ideally package repository a la npm/crates.io
-) Generics. This will help the previous point also. Often it's hard to write proper reusable libraries due to the lack of generics. And no, they didn't keep generics out because they are to complicated for the language users. They just (quote) "didn't find a good way to implement them", and big language changes like that are out of scope before 2.0.
-) Macros probably won't ever come, but if not, please at least give us a language construct that reduces error handling boilerplate. (Rust has some nice macros for that).
I was concerned by this at first as well, but after using Go full time for the past two years, I've never found a need/use for them. Go doesn't lean towards strict OO principles, where I feel that most of the Generics uses are applied (Arrays, sorting, etc.).
Not everyone's opinion, just my two cents.
That's fine if you only use those, but it makes it impossible to write generic functions for much else. See my comment about lack of libraries. I did miss them a lot.
Not when writing your one off app or program, but for libraries.
And you end up having to implement a lot yourself that's for free in other languages.
Doesn't a channel/channel-select fulfill this requirement?
EDIT: the answer seems to be downvote. Okay.
Pushing all "IDE" features into editor-agnostic daemon is a good path for the future. ycmd is taking this spot.
How do you figure? Every time I've been asked for a bio for a conference or similar, they've asked for it in third person. Reading bios in conference proceeding in first person would just come across as weird to me.
e.g. top hit on google: http://smallbusiness.chron.com/write-short-bio-yourself-5728...
And never, ever, ever give up the fight for GNU-style double-dash command-line flags!
Most go programs I've read use spf13's flag library, which is GNU-compatible.
Is Go really expressive?
Personally, I think you're sense of relative value of those has to be pretty unusual to thinks it's "perfect". Though valuing simplicity above almost everything else is what's written on the tin with Go.
" The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. – Rob Pike "
+article: http://nomad.so/2015/03/why-gos-design-is-a-disservice-to-in...
I wouldn't say it is very expressive either. Go can be very wordy and verbose at times, and some things are downright a pain or poorly thought out (e.g, while Java has finally and C++/Rust have RIAA for dealing with resource deallocation Go has defer and it doesn't work really well when you encounter an error in the dealloc logic).
Simplicity is about Java 1.0. Give Go some time to add some technical debt before you get too excited about it simplicity.
It isn't a bad language. This is my second run thought the language, and I'm neither excited nor turned off by it.
Resource management by litigation sounds innovative though
There are projects starting to fix that: https://github.com/asteris-llc/gofpher
Of course it's not as clean as Python, it's statically typed.
But type inference makes code way cleaner than your typical Java code (Javas type inference is improving, though).
No, it isn't since go lacks generics and dynamic types - which helps to reduce boilerplate(even if the latter isn't that effective).
> Of course it's not as clean as Python, it's statically typed.
There are cleaner statically typed languages then python - look at fp languages with type inference(ML dialect)
> But type inference makes code way cleaner than your typical Java code (Javas type inference is improving, though).
The typical java code is legacy, but for a fair comparison you should check out projectlombok [1]. Otherwise, Java is on the JVM which means there are alternatives like Scala/Kotlin etc.
[1]https://projectlombok.org/
* A rust-style borrow checker would make dealing with channels and pointers a lot safer.
* Equality operations for slices would clean up some ugly parts of the standard net library (addresses are []byte, so a direct x == y doesn't work but seems like it should).
* Closed enum sets: some ability to setup an enum type which can be "complete" when used in a switch statement without needing a "default" and which will only panic/fail when a bad type conversion is made. Let me sanitize out a whole bunch of possible errors at one point in my code when I want to use constants (and be warned when I break the guarantees).
-still no math.round WTF?
-make() should be syntax and not this quasi-function that takes this vast array of argument types, and while you're at it, you should just rename it `new`, because that's what it is.
Make decent visual debugger and devs will come.
From talks with coworkers, the impression I've gotten is that most C# in the wild is running server side, though there's obviously a lot of it in desktop apps, and on mobile with Xamarin.
If any language had the tooling and development Java did it would hold the market share. It's my opinion that if Rust makes a similar IDE experience (even just using Eclipse's platform) with every feature like code completion, formatting, debugging, the hole 9 yards and integrate Cargo into it they'll win. But, here's the caveat: It needs to be FLAWLESS integration.
I've been a java developer for my entire programming career. Java was my first language when I was 12 and I'm 19 now. I still can't use javac without looking at a compiler guide since I never use it. Eclipse does the business and I don't ever even need to think about what's going on outside my code. Not once.
We had it in the form of Common Lisp, Smalltalk, Delphi and Visual Basic, IBuilder (for those rich enough), CA Objects, C++ Builder.
Started working and someone showed me how to productively use eclipse and suddenly it was acceptable.
Go is doing well because it realizes we can strip so much away, stay with the standard lib, and get real work done quickly.
That's not to say that Java, in particular the JVM, isn't an impressive piece of engineering, but more that we should be careful when we choose our tools so as to not overly complicate things.
Things like being able to jump to a definition, rename it throughout the project correctly and jump back without even thinking about it are hugely valuable in any language.
And maybe you have ways of doing that in your workflow too, in which case great, you too appreciate the value of good tooling.
I look at it similarly to the way I look at Smalltalk. It's possible to use it as just a series of text files (with GNU Smalltalk), but using it with tools that understand the language (such as Visualworks or Squeak/Pharo) turns it into a very different experience.
I know that a Smalltalk dev environment feels alive in a way that Java doesn't, but using Java with a tool like IntelliJ is a nice experience. Using something like JRebel or HotSwapAgent to swap code changes into a running application also improves the dev experience significantly.
https://blog.jetbrains.com/idea/2011/10/new-in-intellij-idea...
I didn't say features, I said skin. Of course IntelliJ has been around longer.
If you like an IDE that's cool, no hate. I just get frustrated with the idea that I need one to be productive in certain languages. Give me Go and vim, maybe inside tmux, and I'll be productive immediately.
its hard to find good examples of this style of coding. For example, it is hard to figure out which app server (Spring Boot, undertow, dropwizard, wildfly). I'm sure you can use any... but it would be nice to learn from your experiences.
This is an example of a project that is not UI centric, has quite a bit of database integrations (postgres), mailer integrations (mailgun), scheduler (https://dzone.com/articles/distributed-tasks-execution-and-s...). It has also has a bit of math to compute stuff.
Even without a UI - it is something that could be very useful!
I myself have hardly ever needed a debugger in any code i've worked on that has been TDD from the ground up. And it is almost ALWAYS related to OTHER code that was clearly not written with TDD in mind.
Tell me something: What exactly is a "bug"? It is literally just a state the programmer did not expect, correct? So how do you keep all states expected? By constraining them (types, guards, etc.), by testing them in the small (unit testing, TDD), and by making them (ideally) immutable values (preventing unexpected changes to values within one stack frame). If you can control all possible states in the small, you can largely eliminate bugs in the large.
At least, I've found this to be the case after 20 years of working on procedural and OO code...
It's possible, and it works. Read the topvoted comment there and follow its links.
Hence, not tested at all via TDD.
But, I've been working with Go for years and have never used a debugger. I don't feel like I'm missing out on anything.
My only complaint is that they took the CLR path instead of the Ext-VOS one, with is now reborn as UWP (aka WinRT).
Congrats and all, and I love Hugo (use it all the time).
That said, I think the kool-aid was spiked with something.
Sorry, but in what way is Go "revolutionizing"?
I'd buy "refreshing simplicity" and "yay, static builds again" but it's hardly revolutionary, even in its most touted feature (channels).
And it hardly changed how people write software. At best, it introduced some dynamic language programmers to static types and static builds.
All the rest, including creating a language that's easy for average programmers to program at scale was already the goal, and a success at that, of Java.
Really? A lot of copy/pasted code, generics replaced with casts on empty interfaces, poor support for collections, only very primitive concurrency tools, and so on...
I can understand that simplicity is good for small CLI/network tools, but it is hardly an alternative for really big applications where Java is vastly used
Most actual engineering is very far from writing a dumb server for yet another CRUD app :)
Go is supposed to be a systems programming language. That means a very, very wide range of possible domains, where for many of them even network latency is unacceptable. For example, Photoshop would have a terrible user experience if it wasn't the monolith it is. A game engine wouldn't go above ~20FPS (even 60FPS games have a network loop that is capped around 20FPS).
If I can't do systems-level programming in Go, what's its advantage over real systems languages or higher-level languages then? Especially when you consider what Go offers (CSP) comes in opt-in libraries in many other languages.
I feel like Go should be put in the same category as C# and Java. They're just above systems languages but just below scripting languages; giving you the worst of both worlds. Not bare-metal, but also not that productive.
PTC is one such those vendors,
http://www.ptc.com/developer-tools/perc
Go might eventually be used in such scenarios, but the toolchain is not yet there.
The same path that is being followed by those that rely on libraries to reduce error handling boilerplate and go generate, just as two examples.
You may complain that programmers have a professional obligation to learn those new paradigms. You may complain that programmers should consider "learning a new paradigm" a smaller strike against a language, especially if it brings other good things with it. But that won't change observed reality, in which programmers do want to not have to risk several months to a year on something they aren't sure will pay off. By contrast, if you know any of the existing manifestly-typed languages at all (Java, C, C++), which most programmers do to some extent, you'll be writing fancy concurrent code in Go next week. Possibly even "tomorrow". You can be shipping production code in a month.
The language nerd in me really wishes Go had isolated its goroutines from each other and only passed things by value. (While I like Rust's much finer-grained handling of that for a lot of other reasons, you can get a lot of the benefits in a language where the threads aren't supposed to be that large by isolating to small threads, and it's way easier to implement.) The pragmatist in me is forced to reluctantly admit that had it done so it probably wouldn't have succeeded as much, because while that may not have been a "full" paradigm shift if the rest of the language was still imperative, it still would have been at least half of one. That affects how you design larger programs. In a lot of good ways, I think, but still, change with an unknown prospect of payoff.
So... it depends on your point of view. It's not revolutionary in the sense that there's anything new in it. But it is a somewhat revolutionary combination of concepts.
You're even free to join me in being a bit sad that fusing your concepts to an Algol-descended language is basically mandatory for success right now. But, one step at a time. We are slowly creeping away from it. As I've often observed, programming as a profession does not move anywhere near as quickly as some people think; there's a constant surface churn of technologies and languages, but deep paradigm shifts take decades.
Go is popular because it is accessible. The cognitize load of the language is in effect ammortized by being spread over the life cycle of the dev process (i.e. run vet to do x) instead at localized in cycle/space of code-time/source-code, so even professionals not bothered by complexity can see an incentive to using the language.
But I am not aware of the language enabling a class of software systems, etc., that have become more feasable because of the language.
As has been pointed out by many, at some point even the novice programmer has to get serious given the mix of pointers and channels and sync constructs which demands a degree of sophistication, so the accessibility aspect degrades over time.
And then -- and here is why I feel there is pushback regarding Go koolaid -- this somewhat on 'the fence' approach of the language towards concurrency (originally "communicate don't share memory") gives this impression of a glorious hack but a hack aspect to this "revolutionary mix" that you refer to. At some point, the ala cart type system of Go presents issues to people who have invested the time to get serious about these matters but find that their favorite light-weight language is not fully up to the task of keeping up with our conceptual models of thinking about our software.
One of those runtimes is Ruby (Ruby uses lightweight threads, not OS threads), and Ruby is not a new paradigm.
Up through 1.8.x, Ruby used N:1 lightweight threads (multiple lightweight threads on one OS threads.)
Go uses M:N threading (multiple lightweight threads, which may be distributed among 1 or more OS threads.)
M:N threading is a different model than either the Ruby 1.9+ model (1:1 threading on OS threads with a global lock) or the older Ruby approach (N:1 threading), or even the JRuby approach (1:1 threading on OS threads with no global lock.)
For further proof of my point, I submit the fact that you are basically correct, yet those technologies never took off, even in their own communities. The dominant way all the scripting languages are written are as single threaded code, and the second most popular way is as event-based code. It's not easy to bolt that stuff on ten years after the fact, which is what most of those technologies did.
Lua would probably be the better thing to cite as the possible counterexample, in that it does seem like a lot of the people using it are at least using coroutines, though I believe it is not possible/feasible to get them threading in any nontrivial way.
(I'm not taking the out of claiming dynamic typing counts as a "new paradigm"... probably the single largest true programming change in the last 20 years is the mainstreaming of dynamically-typed programming languages. Which I think was still a good thing for static languages because it raised the competitive bar for them; I've personally swung back over to static typing, but the easy of the dynamic world has pushed the static world to greatly improve its cost/benefit proposition, in both categories. If my only choice was dynamic typing or what we had in the 1990s, I'd still be in the dynamic world.)
As a final postscript, may I observe that while I think that Go was a modestly revolutionary combination of concepts, I consider it an indictment of the programming community that it did not happen sooner. I don't see Go as if it somehow won a big race by being the best... I see it as a situation where it's the first thing that bothered to show up, years late, somewhat desultorily.
I'm ok with the Algol part as it pertains to syntax.
Semantics and expression though, is another thing...
By offering simplicity. Taking a step back to before C++. And providing a better C. C + strings + GC + map + slice(array) + json/xml parsing + http transport layer (server & client). What else do you need? Ideal for minimalists. That's its beauty. Programs are very stable. Takes less memory than Java (some amount more than C/C++). Faster compilation time compared to C++. A convenient C. For minimalists, who like to cut down all the jargon, and solve the problem. Looking at it that way, you see its beauty.
edit: typo
I keep seeing this come up and I wonder if people have actually used C in the domains where it matters.
Go is not a C replacement/better C. You don't have control over what goes on the stack/heap which means you can't control memory layout. If you can't do that you're throwing away one of the key reasons you'd use C. Memory layout is critical to getting that 10-50x performance that you can't get in a managed language.
There's a bunch of domains where Go excels but if you're on constrained hardware where you really need the last bit of perf/memory it would not be the tool I reach for.
Its not a replacement for C in most of the places where C is far and away the best existing choice for a whole project, it is a replacement for C in the zone where C's performance and static typing are attractive and would decided the choice in its favor, but Python's expressiveness, batteries-included stdlib, etc. also make it attractive and make it a little bit painful that the other factors weigh in C's favor. For a system that you might be otherwise do polyglot Python (or some similar dynamic language)/C development for that reason (or where you might do C-only somewhat reluctantly for that reason), Go could be a reasonable replacement for C (and, in the case where the alternative would be polyglot, Python or whatever else.)
If your primary concern is performance(which is the only reason I'd reach for C today, esp w/ security concerns) then Go is not going to replace C in that domain.
Well, Swift certainly wasn't when Go was introduced, but, yes, Java and C# were competitors in that role when Go was introduced (Java, even moreso at the time, tended to fall short on expressiveness -- which some argue Go does too, though in different ways -- and C# was deeply tied to .NET, which only had a first-class implementation on Windows; both of those have improved somewhat since Go was introduced.)
https://github.com/dotnet/corert
Many of us are actually mostly working on Windows, so NGEN, RyuJIT and .NET Native are all very good nice toolchain features.
And if I compare Go with the whole .NET stack and tooling, there are lots of things I see missing, in spite of better AOT support across the board.
I'm not really a fan of Go, but being able to write small tools in it without pulling in a huge runtime is a big drawback.
Once upon a time, on the PC we only had static compilation, regardless of the language.
.NET Core would help with the runtime dependencies (XCopy Deploy), but currently it is also WIP for those of us that care about desktop applications.
In any case, when choosing a language for a project which doesn't fit your requirements because maybe the tooling will get there someday, I think you're going to pretty consistently end up in a sticky situation. I like C# a lot more than Go for most things, and some of the reasons are things you listed, but the goalpost shifting is getting ridiculous.
Was C's "static" typing ever attractive as a static type system?
You do have this control. There is escape analysis, but I've never had to dance with it to make it put things on the stack when I need them to. I can get very close to C speeds with Go without much effort.
Your compiler doesn't know your data access patterns, dataset size or a myriad of other factors that you need to tune if you really want to get full utilization from your hardware. To be clear I'm talking about ~5% of software out there, but when you need it you really need it.
But how things are going regarding Java releases, Go could even get generics before Java 10 gets released.
There are lots of cases in which escape analysis falls down. In particular, higher order control flow (closures/interfaces) will always cause it to fail, until Go implements some sort of higher order control flow analysis (which I doubt it will). Go uses interfaces a lot.
I never meant to imply escape analysis was somehow perfect; only that it is (mostly) easy to reason about.
Nothing prevents you to make use of statically allocated data structures, hands off from the GC if really needed.
In spite of lacking a few features that I consider any modern language should have, ability to create memory friendly data structures isn't one them.
Only if you give up closures, interfaces, defer, strings, the entire standard library, and all sorts of other things that don't immediately come to mind.
Programming without the GC in Go is technically possible, but impractical. The language just wasn't designed for that (and this isn't a knock against the language--it just was a non-goal).
We can use the GC for the 90% of the code that isn't performance critical, enjoying the programming productivity of using a GC enabled language.
After profiling the application and find out where those 10% performance/memory critical hotspots are, re-architect the data structures to use static allocation, either via the stack (compiling with escape analysis validation) or the global memory. Or even doing off-GC Heap allocation via syscalls.
Then make use of pointers or array indexes to access the static allocated data.
Plenty of examples in Modula-3, Oberon, Mesa/Cedar research papers.
There are plenty of languages that I would rather use for systems programming instead of Go, but the language is certainly capable of being used in the same scenarios as Oberon.
For example Astrobe, which sells bare metal Oberon compilers for ARM Cortex-M4, Cortex-M3 and Xilinx FPGA Systems.
If anything, comparing Oberon's SYSTEM with Go's unsafe packages, Go could use a few more intrinsics like CPU register access, but those as also doable with a bit of Assembly help. Which ANSI C also doesn't support.
My goal is just creating awareness that alternatives are possible, it doesn't need to be always C.
What I meant was taking a step back to a point before C++ was created. And looking ahead, and choosing what works and is essential (strings, GC,... and some modern Internet usage things...concurrency...etc), and getting rid of all the bloat, which actually comes in the way, especially the opinionated bloat on how software should be done e.g. OO. And making a language based on that.
For some reason, this discussion reminds me of Linus Torvald's angry defense of using C, for git[1] :-)
[1] http://harmful.cat-v.org/software/c++/linus
A quote from "Java in a Nutshell", from (I believe, 2002):
"Because Java is a simple and elegant language with a well-designed, intuitive set of APIs, programmers write better code with fewer bugs than for other platforms, again reducing development time."
Reads pretty similar to your argument for Go. I remember similar quotes for Modula-2, ML, (a lot of versions of) LISP, C itself, and I'm sure there's many more. It seems to me pretty likely that we're seeing the same evolution : Go currently is a simple, easy language that matches the current hotness (microservices) pretty well, because it was directly written to support that. The current hotness will go away and Go will have effectively-mandatory bloated libraries to support the next hotness, just like today it's becoming close to embarassing to use Go's standard library's http server for anything non-trivial.
Additionally, many of the criticisms of C apply to Go. It's looooooooooooooooooong simple code doing "a; if error { ... }; b; if error { ... }; c; if error { ... } ...". Many abstractions just don't work, and have to be "fixed" with crutches (like sorting, logging, ...)
So, I agree with you a lot. There are no guarantees that Go will remain good for ever. It would be a challenge and need effort on the part of the language developers.
That said, I believe, when a language evolves over a period of 20 years. You get the benefit of hindsight to introduce the good things in a new language. So, Java's garbage collection is a big success. And Go developers incorporated it. Its a tradeoff with extreme (C like) performance, and devs went with convenience and security. Good call, in my opinion.
But the object oriented part of Java, in my opinion, is just a C++ inheritance, as at that point of time (mid 1990s). OO was the silver bullet, to all your sofware spaghetti problems. But as it turns out, it got more complex, if the design/architecture was bad. And good engineers design good enough without the OO.
So Go has taken that approach, which gels with me and lot other developers.
Your comment regarding the error handling part. Yes, it is cumbersome. But surprisingly, it seems to look better than exception handling when you write couple of modules with it. And for some reason, which others have articulated better than I can at this time, your programs are more stable as a result.
So overall what I like about Go, is the minimalist approach of just having the proven things which are good. And avoiding which are grey area or bad.
Language consistency (no magical functions like "make" or magical items like slices), DRYness (generics), better type-safety than C (interface{} is the void * of the 21st century), would be a good start.
Due to the mandatory GC, it's also not a C replacement where C is good at. Hence the backtrack on "systems", etc.
After posting, I realized its concurrent programming model, is another thing which I (and many others) am a fan of. I found the channel and go based approach, much better compared to explicit spawning of threads in languages like Java and C++.
I tend to complain on Go's design decisions and don't have any actual use for it, but given my experience with Oberon, I do believe there are use cases where it would be possible to use Go as systems language.
For sure not in when targeting 8 or 16 bit CPUs with a few KBs, or timing constraints that cannot deal with possible GC pauses.
But for more beefy micro-computers like the original PI, it can certainly be an option.
This would a good starting point for anyone trying to attempt it
http://wiki.osdev.org/Go_Bare_Bones
Also I consider the ability to write compilers to be systems programming use cases and Go is already bootstrapped.
Having said this, yes there are better options to do systems programming with less need of Assembly help.
Can confirm (well, add another point of anecdote), I played with it a bit in order to get some experience with static stuff before diving into the whole C ecosystem.
Next thing I build that needs that kind of development will probably be C though.
In Java println is checked for errors, as it should be. Every Java program that does println either aborts or deals with the error in some way.
It matters. Take any program that writes to stdout and run it with ">&-" to close stdout. Because of unix, the next file it opens will be file descriptor 1 and the program will start writing the console output to the file. You want the program to abort instead of trashing a file, and especially not to trash /etc/passwd.
That's why you don't want to ignore errors, because any time you do there can be unintended and hard to imagine consequences -- even something as simple as writing to the console. Simply put, Go encourages buggy programs with poor error handling.
What "next file it opens" are you talking about exactly? How does what you're describing work?
Example:
Output: By ignoring the error, the program continued on and then wrote console output messages to the file after it was opened. There have been exploits due to this bug, but the real point is that you could never predict this failure without good knowledge of unix and careful consideration. This is why error codes should not be cavalierly ignored, because it's really hard to know what might happen if you do.Last I checked, Go operates the same way as this C example. Java fails on the "first message" if stdout is closed so it doesn't trash the file, not because they even specifically thought of this scenario but just because errors are not ignored by default and are not easy to ignore.
No, you don't panic, you are most likely to corrupt your data and maybe continue working as if everything was fine. If you are lucky, your code panics.
> ... you already know where to start debugging.
Grep all occurrences of "_"?
> That's much better than having an exception bubble up from deeeeep inside your code with some context-less cryptic message or number, isn't it?
No, the exception carries all the context you need. If you ignored a previous exception, the next one won't be as useful as it could, but at least your code breaks loudly.
There's no way to know whether the programmer actually considered all the error cases (they are generally very poorly documented in Go code), and there's no way to know if the called function added errors at some later date. If it's though an interface, there's no way to know that all implementations have the same errors.
It's really an 80s-era approach to error handling, and that's bad.
I don't see how that follows. A stack trace (as initially cryptic as it seems) is a record of all the code that would have affected the current buggy state you find yourself in. If anything, a stack trace isn't comprehensive enough, ideally it would include all known/named values at every level of the stack... a large quantity of information, to be sure, but perhaps it could be narrowed down by diffing each stack level with the previous one or something. But if you could have those 2 things, you would have literally ALL the information you needed to fix the bug. Since bugs are programmer-unexpected states, and the stack trace plus the known values at each stack level are LITERALLY the entire state.
So correct me if I'm wrong, if you don't assign an error to a variable on the same line in Go, it throws? Because it was my understanding that there were absolutely no throws at all in Go. Or is it only that certain kinds of statements are expected to possibly error, and must therefore be assigned to a variable (or _)?
Assigning to _ effectively ignores the error, but is considered a very bad practice.
Unavoidable runtime errors (eg divide by 0) will generate a panic, which is similar to an exception, but is uncatchable within the Go routine it originates in.
Nonsense.
I just ignored an error and it compiles fine. Go check the return type of Println:https://golang.org/pkg/fmt/#Println
tl;dr, it's very easy to accidentally ignore errors in functions that are side-effecting or modify parameters. Been there, done that.
"Syntax error, expected token HTTP_METHOD but got 'B'"
In Go, with proper error handling I'd get a message like:
"Couldn't do flubber transfer: Unable to negotiate protocol: Sent supported protocols but remote responded with HTTP 400 "Bad Request": Missing required field 'version' in protocol definition"
As "error" is an interface, this message is only the tip of the iceberg: the error object itself may contain the address of the remote server, the protocol definitions that were sent, the response body that we got and any other relevant contextual payload.
This is all due to the error being treated in-context as opposed to just bubbling up.
Well yeah, if you handle errors properly, you get better results than if you don't.
The point is that with exceptions, it fails loudly and you get the cryptic message even if you forget, whereas with Go, if you forget to handle an error, you just get weird behaviour and corrupt data.
And if you want more helpful error messages, you can catch the exceptions and report the context just as easily as you can in Go.
> I don't see how that follows. A stack trace (as initially cryptic as it seems) is a record of all the code that would have affected the current buggy state you find yourself in.
You get stack traces in Java, C#, Python, etc. You don't get them in C++ (well, you aren't guaranteed to get them).
https://www.youtube.com/watch?v=lsBF58Q-DnY
I frankly don't see how this is OK, nor better. Errors are real in the sense that they are evidence that the programmer's mental model of state did not line up with the actual state (usually in some corner case). There is literally NO circumstance in which being in a programmer-unknown state (yet still "alive" so to speak) is better than being in a programmer-known-and-accounted-for state. (For an example of being in a programmer-unknown yet alive state, see: Every Security Hole Ever.) This is why "throwing" is appropriate, no matter how much the Go creator didn't like it. If even a single runtime error goes undetected/unhandled, you will end up further and further into an unknown state due to state corruption, and your behavior will soon become completely nondeterministic (or at least, nondeterministic-appearing). Losing determinism is the first step on the road to dragons.
I literally can't see how this wouldn't happen, and what I AM seeing is a lot of chatter in Go communities about how to manage this problem, when the solution is already there: Just fucking throw a stack trace. Or, even better, do what Erlang/Elixir do: Throw it, log it, and have a supervisor process kill it and restart it, all in 1 millisecond or less. If the error keeps happening above a certain threshold rate, kill and restart the supervisor as well. The reason this works (and has resulted in the INSANE uptimes that Erlang-backed services have) is that it resets state to programmer-known conditions.
On top of that, unwinding isn't the only way to get something where errors have to be handled or the program dies. Look at Swift's error handling for example: it's basically sugar over returned errors with correctness checks. Dead simple implementation, and dead simple to explain to users.
What I don't buy about the justifications for Go's design is that they were driven by the pure guiding hand of simplicity. It looks more to me like the designers simply treated language features they had seen done badly before as anathema and didn't try to think about how the mistakes could be corrected.
> Not really.
I can assure you that it is indeed funny.
One thing I liked the most about c# was the libraries in one place.
One thing I liked the most about PHP is the many array methods and easy to call methods from objects.
One thing I liked the most about Python is the readability and built in debugger into the language.
One thing I didn't like about all of them is all the libraries you have to memorize.
Steve was the only one of the 3 that I interviewed with that didn't come across as a self-important asshole. He was kind and helpful and overall seemed like the perfect guy for a Developer Advocate or whatever the title was. I wish him luck.
Oh I hate that type of interview. I had a similar experience getting interviewed for a front-end dev position by two backend devs at a consumer VPN company in Toronto who asked me to write common CS algorithms while they both sat there staring at me.
Even ignoring the total lack of connection between the content of the algo with the job in question, I've never coded like that in my life. Largely because I also never took a CS course and didn't bothered to study cliche interview questions because I don't apply to big cos. But most importantly, I don't solve problems on the spot in front of other people IRL, so what aptitude are they testing for exactly?
I'm an introvert who always codes with headphones on and likes to spend time thinking through problems on my own - but can also work with other devs to solve problems when needed. I also had OSS projects, private repos I could show them, and live production apps with tens of thousands of visitors I built myself to point to my aptitude. That's been more than enough for other smart teams to hire me. It's also the content I look for most when I've hired people myself.
The non-technical founder also never told me ahead of time they were going to do this type of interview so I was surprised when they sat me down.
Also during a telephone interview with their other semi-technical founder asked me to describe how NAT works (yes asking a front-end dev to describe network implementation details). I figured he was just confused about what I would be doing for them... then I showed up to meet the two backend devs the next day.
I just walked out of the interview and told the founder his hiring process was silly, even though I liked the company (product). I'm happy I did.
I should note that I'm not against the idea of measuring aptitude during the hiring process, if companies want to be thorough. For example. during another interview process with a different company a dev team gave me a problem to solve (with ample warning before I arrived that day) and let me solve it on my own, using my own laptop in a quiet room, while they went back to work at their own desks for an hour or two (taking however long I needed). I was fine with that process and it worked out. There was also a collaborative element to it as well. That was a test better grounded in the reality of software development.
In fact, if I recall, we asked you to reverse a string. This is not an "algorithm" question, it's a basic programming aptitute test and if you can't do it on a piece of paper, this is a major red flag for us. Furthermore, you demonstrated a lack of understanding of basic JavaScript concepts like prototypes and scoping. This, and your personal attitude, were the reason why we did not hire you for the front end deveoper position.
You are no doubt a hardworking and resourceful individual, but we were looking for something more than ability to glue JavaScript libraries together. I wish you, and "other smart teams that hire [you]", all the best.