I've written a lot of Go, I started four years ago when my workplace at the time wanted to ditch writing web services in Ruby. We were very deep into containers, so the obvious choice to the team lead was to do what the Docker folks were doing.
My take is that Go is a great language when you're working with incompetent people. It's so limiting in the abstractions you can build with it, that it is very difficult to write code that is not immediately obvious in its purpose. Go is easy to teach, easy to review, and it's so syntactically deficient and the tooling is good enough that it's hard to bikeshed. This ultimately improves the experience of writing code at work, and its static type system makes it easier to reason about large code bases for the folks who are used to using Ruby and Python at scale.
But Go is also awful in so many ways, its such an obvious step backwards in its design and the community is so dogmatic about Go and the Go authors.
On large code bases you almost invariably end up working around the deficiencies with the empty interface, code gen tools that add non standard syntax, and lots, and lots of copying code and code patterns over and over again. Any common pattern you see emerge, that you'd like to create a generic structure for, is probably impossible to encode without type variables. Go also notoriously pushes many errors that would have been caught at compile team to runtime because of its weak type system. With Sum types, it's difficult to make improper states illegal, and default values lead to subtle bugs because they essentially behave as predictable garbage.
Go honestly keeps me from having faith in software engineering as a craft. It's the acceptable of the status quo, or worse, moving backwards in order to fit the needs of the lowest common denominator.
"My take is that Go is a great language when you're working with incompetent people. It's so limiting in the abstractions you can build with it, that it is very difficult to write code that is not immediately obvious in its purpose."
Do you think this is intentional? Similar to how java is incredibly restrictive, and thus great for corporate programming environments?
“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.”
Yet Turbo Pascal for MS-DOS, with more features than Go has currently, did already have faster compile times on those clunky 8086 CPUs, let alone on modern 21st century hardware.
Fast compilation times in Go are only a surprise for a generation that never used native programming languages with native support for modules.
As for C++ compilation times, yes they are an abomination when doing make world from scratch, including third party dependencies, with heavy template code.
However modules are around the corner and they were just voted into C++20.
I feel that there is a certain irony that while Java shared some of Go's goals in terms of target audience, is currently much more advanced than Go is, or might ever be even if all Go 2.0 goals get fully implemented.
I have come to see it as a tool for the folks that want a plain better C and are happy with having a GC around to keep them productive.
This is why I do not think adding generics is a good idea. Go excels at the areas I described. There's obviously a need for a language that is limiting, and many people enjoy Go for that reason.
> Which specific "code gen tools" and "non standard syntax"?
I've done codegen through a DSL and through comments. The DSL is actually typed, which is nice, but it's also its own mini-language that you have to teach people on top of the Go basics. It's also a general rule that it should be used sparingly because it tends to generate very verbose and ugly code.
> What kinds of errors would you expect to be "caught at compile time" with a different type system?
The empty interface, which must be cast to the contained type to be useful, is an obvious one. Generally type casts, error handling, pointer dereferences, all can lead to run time bugs.
At the macro level, a language with a rich type system should make invalid program states fail to compile. With true phantom types, for example, one can make a state machine that will fail to compile if a state transition invariant is not satisfied.
> What kinds of "default values lead to subtle bugs"?
The issue is that the notion of a "default value" makes little sense. For many applications 0 is not a good choice for the initial value of every integer. Someone who forgets to set the integer to an appropriate value will run into the same error as in C, except instead of a trash value, they will find a consistently bad value, which is slightly better.
Better to throw an error if an uninitialized value is used, and demand that all struct fields be set unless the struct author has explicitly set default values.
> Have you looked into...
Yes, I have seen the proposals. I am an opponent to generics in Go. I think Go excels at exactly that which I described in my first post. If you add more to the language, I believe it will morph into a poor mans Java instead of a better Go. Better to simply move to a language that was built from the ground up with the intention of providing abstractions (I like Rust), and use Go when its upsides outweighs its downsides. Many times, they do, especially in businesses where the talent pool is limited.
In the last 3 months, we have built the new version of our API in Golang, the old one was in rails. The api serves a mobile app and a web app.
Despite taking longer to build, it's a treat to work on Golang. The explicit error handling and static typing have made our code much more reliable, with a lot less unit/integration tests required. Concurrency is dead easy to understand and build over. We are OK with using dep, definitely miss a debugger or an REPL. Having generics would make it much easier to write code. But overall it was worth it. Our response times are much lower now with a lot less server nodes required.
Yes, I actually meant VSCode, hn doesn't let you edit comments after a while and I wasn't completely aware of the differences between Visual Studio and VSCode. I checked and I'm actually using VSCode (in ubuntu).
Bizarre that a programming language designed by non first time language designers, backed by a multi-billion dollar company and used extensively throughout that company(by "new" engineers), doesn't have a world class debug story.
> Having a debugger available does not drop your IQ by 20 points.
Not necessarily, but it can have that effect. Sometimes I spend a long time in the debugger, following the chain from effects to causes to the causes’ causes and so on, getting a little dopamine hit every time I uncover a new level – it’s exciting, like watching a detective show – and ultimately discovering the root cause… only to have it be something dumb or obvious, something I might have found in less time by just patiently rereading the code.
And I find this expectation rather bizarre. Since when compiled programming languages ship a debugger?
You can use a world class debugger, known as gdb, with Go, and yes, they do provide extensions for gdb to make it understand high-level Go constructs (such as goroutines, maps, slices, channels, ...): https://golang.org/doc/gdb
What you say has nothing to do with languages and it precedes 80s; Watcom, VC++ etc. comes with a debugger because the proprietary system they run on doesn't have one. GNU/Linux always had gdb.
So if you want me to spell it out, here you go: it's bizarre to expect a debugger from modern compiled languages which have been FOSS from the beginning.
What fails? Some commercial C/C++ compiler producers invest into a debugger typically because some of their customers use it on a platform without a good built-in debugger. Yes, some systems had debuggers. The point is, some didn't.
I said compiled language, which Python isn't. Yes, gdb won't help you debugging scripting/VM languages.
Anyway, this is getting off topic, as we're talking about the alleged lack for debuggers for Go binaries for today's Go users, and neither of the things you mentioned are relevant. In my experience, nitpicking rarely leads to anything fruitful.
Those some commercial producers as you put it, have invested in debuggers for BASIC, Pascal, Modula-2, PL/S, PL/8, NEWP, C, C++ and Ada for IBM/360, VAX/VMS, IBM 400, Solaris, Aix, HP-UX, Tru64, Irix, by IBM, Unisys, DEC, Compaq, SGI and HP themselves.
And if I have to mention a FOSS compiled language instead of Python, with debugger support out of the box, then Oberon, Modula-2, FreeBasic, Gambas and FreePascal come to mind.
I use pprof to find and fix bugs in Go apps. I just recently used pprof to find and report a huge memory leak in coreDNS (when using it as an authoritative DNS server for very large zones). I was able to find the bug and get it fixed in less than 24 hours.
It’s _fine_, but I rarely (almost never) find myself reaching for it. If I need the debugger, it’s probably a sign that I should be improving the logging so I can troubleshoot production issues.
This is especially true in Go where the type checker gives you a lot of confidence that your code works before you run it.
How are you able to write code without being able to set breakpoints, stepping in a function, do mouse over to see the value of a variable? Do you print values of variables when you need to?
I found a video of someone debugging Go code in VS Code, and I saw them set breakpoints, step around in a function, and hover over a variable to see its value, in addition to the sidebar containing all of the values of all of the local variables and the stack traces.
It helps to write small functions with good interfaces and plenty of tests... The better your fundamentals, the less you need the debugger. (But, also, there's gdb and delve for debugging. So, do what ya wanna.)
Not op, but I don't think people would want to go from Ruby to Java. I was working on Java before I was a Go dev, feel, Java is too verbose. Go is so simple to start with and you don't need "frameworks" for deploying a production quality microservice.
I haven't used dropwizard, but used Spring boot extensively. While it definitely helps me move faster and write less boilerplate, it's still way too verbose relative to Golang.
And talk about the build size, time and the fact that you could generate a build and run it bare metal anywhere.
I love Go, used it a lot for over 3 years. I love the simplicity, directness, the inline structs, but I still cannot get out of the package management mess. I can even tolerate `if err != nil, return err` and do not care enoguh of generics, but I hope Go modules and Go 2 would fix, over the time, it has really become a love-hate relationship for me. My daily driver at work is Java and some Python, I really miss the tooling (of course, not runtime classnotfound errors) and maturity. Not ignoring the success of projects with Go, such a big one as Kubernetes, I think it is a whole different aspect, but Go started to tear me out through the years, and it is hard to defend it.
We've transitioned entirely to Go modules. It's been largely painless at the codebase level. Being able to ditch GOPATH is fantastic. It helps that "go mod init" includes automatic migration from Dep. So far we haven't run into any serious bugs; compared to Dep (and Glide before it), "go mod" just works.
However, the toolchain is lagging [1], sometimes in quite serious ways. Pretty much every third-party tool that reads Go source code — linters, code generators and so on — uses an unofficial, largely undocumented package called golang.org/x/tools/go/loader to parse Go code into AST structures. This package has not been updated to understand Go modules, and so it fails, and it has been abandoned in favour of a new, also largely undocumented library, golang.org/x/tools/go/packages, that works completely differently. There's a tracking issue [1] that discusses the progress of converting these tools. There's no document that describes how to migrate.
It's annoying because we've come to rely on some, such as Mockery and go-sumtype, that still don't work with Go 1.11. In both cases the authors seem to have abandoned the projects, and updating the code isn't always so trivial.
I don't think you really mean golang.org/x/tools/go/loader or golang.org/x/tools/go/packages are literally undocumented; simply pasting the URL gives you pretty detailed godoc for both packages. Rather, there is no official "how to load Go packages guide" pointing to those packages, and tool developers probably discovered those only by reading other source code.
FWIW, the .../loader package is explicitly documented as experimental. The .../packages has a deadline of 1 Dec 2018 for breaking changes (both info found in their respective godoc)
The tools team is still working on go/packages. It is bound to get better.
Given that modules support is still officially experimental, there are bound to be rough edges. But by 1.12, I think we will have a solid modules story.
I have no love of Go nor of Java, but Python's packaging is a nightmarish tower of babel built upon a mountain of spaghetti.
There are so many incompatible, NIH wannabe solutions to packaging in Python. Recent tools like pipenv have made things easier from the user standpoint, but if you dare to look at how the sausage is made it's still an absolute mess.
Even if newer packaging tools are better, there are still tens of thousands of packages that use the older, nonstandard, even messier solutions.
There is no better way to do errors imo, unless you're going the erlang way of "let it fail". Errors can be passed to the caller the same way exceptions are, and it becomes an endless pursuit of where the error is actually being handled (if at all). I agree that some syntax sugar helps, see Rust, but it doesn't completely fixes the problem.
It's baffling though that the compiler doesn't at least warn when you forget to check the err. So many edge cases with that (especially due to `:=` vs `=` if reassigning a prior err).
Exhaustive pattern matching combined with ADT seems way better to me compared to the go error handling where it is pretty much guaranteed that you will shoot your own foot forgetting to handle some errors...
> I love Go, used it a lot for over 3 years. I love the simplicity, directness, the inline structs, but I still cannot get out of the package management mess.
The problem is the package managers available are fragmented and fundamentally incompatible. You have dep which is the official “experiment” and 10 others that share no compatibility whatsoever. Until there is either an official package manager or at least a standardization of the package format, you will run into these issues.
I think you missed the last 6 months of development regarding Go dependency management.
It's even mentioned in the blog post: "Last spring, we published a draft design for Go modules, which provide an integrated mechanism for versioning and package distribution. The most recent Go release, Go 1.11, included preliminary support for modules."
I think go modules are still experimental, and thus many companies and projects have not yet converted. This has been my experience anyways; I'm still using dep at work.
Because go modules came later, it will take a while for the community to convert, and there will still be dark corners running on Glide for probably another year or two (or longer).
>Go 1.11 includes preliminary support for versioned modules as proposed here. Modules are an experimental opt-in feature in Go 1.11, with the hope of incorporating feedback and finalizing the feature for Go 1.12.
https://github.com/golang/go/wiki/Modules
They are officially present and will be compatible in some way with future releases, but its still having the kinks worked out.
No, I didn't miss hearing about it, just like no one missed hearing the Python 3 release.
You've got nine years of open-sourced Go...even in the best case scenario, it'll take more time that six months after a draft design for this to not be a common, massive pain point.
How do you keep track of dirty writes on structs? I want to only persist if the struct was actually changed. I could add getters setters, but a lot of the Go community seems to say I’m stupid for wanting to do that. I could track externally from the strict, but that seems backwards.
Sure. I have a Profile. Let's say I load it from the database and make a change. I want to know if I need to save the profile. This is particularly important when using optimistic locking, a.k.a. resource versioning. My profile has a version number, like an ETag. I only want to update if there is a real change, and updating increases that revision number with every persist.
Here's a scenario. A woman gets married. Her last name changes. She updates the profile from Smith to Jones. The last name is now dirty. The code that save the object can say, "Are you dirty?" The struct says, "Yup". Saving code says, "Right! In you go." On the other side, the bit gets flipped back to false (it's cleaned). At the end, the revision updates by one and goes back to the client.
Another scenario. A user opens the edit profile box, doesn't change anything, but hits save. Without a dirty check, the object, which hasn't fundamentally change, gets saved with a new revision number.
Now what is all this about the revision number? I don't want to allow changes to a profile whose revision number is greater than the revision number my client passes. Let's say the Profile has revision 5. The client, which has been disconnected for a bit, says, "Update the profile, at revision 2, with all this new great data." The system needs to reject that update. The client needs to tell the user, "Sorry. That didn't work. Here's all this new data." If the client is especially Canadian, it will also say, "Oh, by the way we've saved your data, would you like us to copy it over the new data". I've never seen Canadian code, and mine is especially Floridian, but it could happen like that.
All of this is fairly easy in OO languages, which Go is not. It's actually easy in Go too if you use -ters and Setters (Go doesn't like GetFirstName(), but is okay with FirstName()). If I use functions attached to structs, easy. Just curious how other feel about this.
I first heard of that in context of writing to the screen, actually, when you don't want to redraw all the things every frame, or simply can't afford to, and only draw the things that have changed (i.e. are "dirty"). So if you were to keep track of the old position of an object, you might redraw the area of background where that object was, and then just draw the object at the new position, instead of drawing the background for the whole screen, and then the object.
Browsers are also kinda heavy on this when it comes to both layout and drawing. E.g. when the content of a div with fixed size changes, you only have to layout and draw the text in the div -- but if the size isn't fixed, you might relayout and redraw everything on the page that follows the div, but might not need to touch anything that comes before it. I'm sure it's incredibly complex, but still much faster than not doing it.
Maybe store a hash of the values in the struct and check against that too see if anything has changed? If you're doing lots of dirty checks, that might be too resource intensive, though.
Or just use getters and setters and if anyone calls you stupid, ask them to demonstrate a better solution that still solves your problem.
I prefer to use .NET Core / C#. It is very fast, async, cross-platform, open source and unlike GO, it is a high level programming language. One disadvantage is that compilation time is not as fast as with GO, but it was improved over the last years.
PHP and JavaScript are not statically typed.
What would be the use of generics in there?
Obviously for the same reason you don’t need the dynamic keyword.
As for LINQ at least in JavaScript you have it, either integrated in the language with map, filter, reduce or using some library like lodash or Ramda.
You don’t have expression trees but you don’t really need them given the highly dynamic nature of the language, although not at the same level as ruby if I remember correctly.
JavaScript has exceptions, so here I’m really not sure what you are speaking about.
For FFI marshalling (that for sure I would not call high level feature) there is webassembly.
Match expressions are present in JavaScript and obviously you can’t have exhaustive pattern matching like in typescript because of the lack of types. Even in that way it’s pretty comparable to c# switch case.
So to me it seems that at least JavaScript has all the features listed in the gp, why you are convinced of the contrary?
I'm in the process of evaluating Go-raylib for a fantasy console project in lieu of Love2D. The basic rationale is:
* I only need inner loop high performance - it's a fantasy console, and intentionally limited. It will have some C code, but I don't want to write a lot of it.
* I want the project to be easy to build with few external dependencies(raylib is tops at getting this right)
* I want static types and zero-indexing for the "OS layer" part, at least, since I have to build some original data structures for IDE functionality(e.g. there will be a text editor).
It was the last part that made me switch away from Lua, and then it was the first two that made me consider Go.
I think the problem is the ffi overhead to C from Go is really high, like, way higher than any comparable language, because of the segmented stack thing. I mean the nice thing about Love2d is that LuaJIT has the best ffi overhead when calling C.
If there is a link, I'd say it's devops —except cockroach and caddy of course.
Go is a very useful language for this space, as it's easy (we aren't developers after all), it's safe, fast, compiles to a single binary, easy cross-compile support, software written in it tends to be robust, great basic library, etc.
My take is that Go is a great language when you're working with incompetent people. It's so limiting in the abstractions you can build with it, that it is very difficult to write code that is not immediately obvious in its purpose. Go is easy to teach, easy to review, and it's so syntactically deficient and the tooling is good enough that it's hard to bikeshed. This ultimately improves the experience of writing code at work, and its static type system makes it easier to reason about large code bases for the folks who are used to using Ruby and Python at scale.
But Go is also awful in so many ways, its such an obvious step backwards in its design and the community is so dogmatic about Go and the Go authors.
On large code bases you almost invariably end up working around the deficiencies with the empty interface, code gen tools that add non standard syntax, and lots, and lots of copying code and code patterns over and over again. Any common pattern you see emerge, that you'd like to create a generic structure for, is probably impossible to encode without type variables. Go also notoriously pushes many errors that would have been caught at compile team to runtime because of its weak type system. With Sum types, it's difficult to make improper states illegal, and default values lead to subtle bugs because they essentially behave as predictable garbage.
Go honestly keeps me from having faith in software engineering as a craft. It's the acceptable of the status quo, or worse, moving backwards in order to fit the needs of the lowest common denominator.