Go 2, here we come
blog.golang.org
blog.golang.org
It is bizarre to me that languages boasting built-in language-level data structures like lists, hashtables, etc are content to just leave us with the bare minimum support of numbers, being basically whatever the hardware thinks a number is. The semantics of integers and fractions are perfect, and everybody already knows them. On the other hand, overflows in int32’s are weird, and if your idea of a fraction is a floating-point number, then you can never have something like (5/3)*6 evaluate to 10 exactly.
To be clear, I think fixed-width integers and floating-point numbers have their place, I just see no reason why they should be the default.
class Num a where
(+) :: a -> a -> a
(-) :: a -> a -> a
(*) :: a -> a -> a
negate :: a -> a
abs :: a -> a
signum :: a -> a
fromInteger :: Integer -> a
"3.0" would be "Fractional a => a" which means it defines the functions in the typeclass Fractional (in addition to being a Num): class Num a => Fractional a where
(/) :: a -> a -> a
recip :: a -> a
fromRational :: Rational -> a
Depending on how you use the value, the type would be further refined in compile-time. For example, if you did `recip 3`, 3 couldn't be any Num anymore, it would have to be some Fractional.Regarding (5/3)*6, it does equal 10 using floating point. A better example would be how 0.1 + 0.2 is not equal to 0.3. We can see this:
ghci> 0.1 + 0.2 == (0.3 :: Float)
False
If we specified that we're working with Rational values, which are also Fractional a => a, and which are defined as a pair of integers, one representing a numerator and the other a denominator, roughly like so: type Rational = Ratio Integer
data Ratio a = a :% a
then we can see that 0.1 + 0.2 does equal to 0.3: ghci> 0.1 + 0.2 == (0.3 :: Rational)
True
It's pretty cool that Haskell lets you define new types of numbers and use them like any other. While you can transparently support hardware type numbers, represented by types like Int, Float, Double, Word, Word8, Word16, Word32, Word64, you also have transparent support for arbitrary precision Integer and Rational. Writing your functions to work with the typeclasses like Num and Fractional lets your functions work with any of these types and future types defined.I love all of the automatic casting between exact types, but happily implicitly casting to inexact types is (in my opinion) a big mistake.
What do you mean? As far as I'm aware, you always have to explicitly convert exact types to inexact types in Haskell (using "fromIntegral" and "realToFrac").
> Coupled with the fact that so many standard functions (like length, for example) want to return an Int rather than an Integer, it just leads to me spewing ((fromIntegral ___)::Integer) all over my code.
Indeed this is rather annoying. It's this way because of legacy reasons (see https://www.reddit.com/r/haskell/comments/60u10b/do_function...).
As a long-time Racket and Scheme user, I'd say that arbitrary precision numbers as default bring more disadvantages than advantages. As an option with syntax support Yes, but not as a default. it just makes it harder to port all kinds of code that relies on modulo arithmetics.
If you're relying on modulo arithmetic, you should be using a fixed size integer anyways, not one that varies based on the architecture. Go has fixed size integer types available, and modulo arithmetic should be using those, not "int".
Rationals aren't supported natively by processors, so there's no real need to handle this as a primitive instead of letting people to use a library for it. Adding them to the base language just because some people would find it convenient would clash with Go's explicit minimalist philosophy.
It's generally nice when you can do simple things with the language's built-in standard library. I tend to prefer languages with more powerful standard libraries because it means that you can more easily move across codebases since they'll all be the same. If commonly used data types like rationals, hashes, maps, strings, etc., aren't defined in the stdlib, then there'll be any number of different libraries to use them, and you'll have to potentially re-learn a lot of different unnecessary things when working on different codebases.
I'd say that would fit in quite well with the brand of minimalism that Go is representing, keep the language simple and provide builtins where more convenience is needed. The opposite kind of "minimalism" would be providing tools to allow libraries to define what other languages can only support as special builtins. C++ and Scala went down that road, they minimize builtins by empowering libraries to replace them. That's also a form of minimalism, but not the one chosen by Go.
Tangent: now I wonder wether Scala has inerited special handling for the (not special, but specially handled) string type it inherited from Java or if just reimplements those syntax extras in the standard library using implicits.
Rationals are also the default number type.
Lists are the base type used throughout Scheme, and can be used to implement arrays and hashmaps and whatnot, so Scheme doesn't really need them, you can just use a library.
... That didn't stop the committee adding types to the spec. Like records, promises and other complex types.
What is useful and necessary to a language isn't defined by what the processor can natively do, or we'd only use registers, not arrays. And it isn't defined by a overbearing attitude towards one or more philosophies.
Probably a good linter idea in there somewhere.
However, having made the mistake a few times now of trying to use "unsigned ints" for things that I want to assert are never negative, I've learned the hard way not to do that. The problem is, there's always some bug you have in the program that will drive the uint "negative", wrapping over to a huge number. Unfortunately, since you get no warning when that happens, it tends to take longer to show up, and without a clear cutoff to say "ah, this is clearly invalid" it can be hard to even program detection code, whereas checking for "less than 0" can be unambiguous.
I'd loooove it if I could easily, cheaply, and universally (i.e., not just in Go, but across all my programming languages) turn on a behavior that says "if this int or uint under or overflows, throw an exception instead of trying to do whatever stupid thing you're going to do". I get the sense this is heading down the road where in 20 or 30 years, this is just going to be common sense. However, we are very early on the road yet from what I can see. Whenever I bring it up, even today, even in this sort of context, it still is generally negatively received. (But not uniformly. A few other people agree. I think momentum is with me. I'm not sure my programming career will live long enough to see it, though.)
If x is unsigned, then you can’t rearrange an inequality like x >= 4 into x - 4 >= 0, since the first inequality is only true sometimes, whereas the second is true always. This looks obvious, but bury it a little way into some nontrivial index arithmetic and boom.
Another more aesthetic reason is that indices can represent offsets into a structure relative to some other index, and in this case you really need negative numbers.
I want to emphasize that I do not know the true answer to this question.
I think that indexes are int to play nicely with a decrementing loop counter. Doing:
for i := len(n) - 1; i >= 0; i-- { fmt.Println(n[i]) }
requires i to be signed due to the last iteration.A modern language with this type of confusing and platform dependent behavior is inexcusable. By all means, include a native data type as big as the processor can handle (size_t), but don't call it int which is the default data type most people will use out of pure laziness.
Defaulting to 64 bit math on 32 bit systems will have a huge performance penalty on generally the slowest/oldest systems where this penalty is least desirable. It's not clear to me what the benefit to this would be for most programs.
Running 32 bit ints on 64 bit systems will cause problems handling large arrays on systems with a lot of memory. I think it also complicates generating efficient loop code, although C++ gets around this with signed integer overflow being "undefined behavior".
Of course Go has int64 and int32 types if you want to use them (and all conversions require explicit casting), but the default type for array lengths etc is the platform native type. But what is the better alternative?
Unfortunately I've read every comment in this HN thread (so far) and I haven't found any specific one mentioned, just people calling it "crazy" and "confusing" over and over.
Go is a language that has explicit pointers! And of course those can be 32 or 64 bits and change struct sizes, field offsets etc. There are certainly things about Go that are confusing but I've never thought this was one of them.
Just FYI Go has int8, int16, int32, int64, and int types. Only the 'int' type is the machine-native one. It's always been obvious to me that 'int' is the one without a fixed size, but I think this would be less obvious if the naming convention was different using short, int, long etc.
But I still think that native types have their place. It can often be quite reasonable to accept serious limitations on narrow machines where the performance gains won by the trade-off are desperately needed, lift those limitations on wider machines and grant an enormous safety margin on the widest architectures. When you dive deeper, the values where this makes sense still tend to be "index-like", as GP stated, but they don't have to strictly be indexes (e.g. IDs, a wider machine will be capable of working on bigger datasets and therefore be more likely to exhaust a given width of IDs, all indexes are identifiers but not all identifiers are indexes)
As for the int vs int64, I definitely run into what I describe on Project Euler. Probably using a self referential map[int]int when I need to suddenly pull an int64 and everything gets infected.
I realize it's just a matter of convenience for me, but I personally have no downside to int == int64.
Take averaging as a very simple example. Doing (a + b) / 2 will overflow if a and b are sufficiently large, even if the average will always fit in 32 bits. Things like this go unseen for years.
I typically wouldn't be passing around milliseconds since epoch as an raw number, but then that's C#, I guess you might do that in other languages for performance or out of necessity.
My main issues with it are:
1. Like many other performance optimizations, it is a trade-off. You may be sacrificing easy maintenance or even introducing breakage (if the person using it doesn't understand the limitations and implications). So IMHO you should only do it if you can show through profiling, etc. that the gain is real and worth it.
2. As a matter of language ergonomics, it should never be something that anyone does unknowingly. You should have to explicitly ask for it. The type name should be something blindingly obviously like "native_int" or "word_int".
On CPUs with 64-bit and 32-bit registers, that typically is the register-sized integer type. Machines with 16-bit registers are a bit of an edge case, as 16-bit integers may both be a lot faster on them then 32-bit integers and, in many cases, too small)
If go chose to use 32-bit integers as default, some users on 64-bit systems would complain that they couldn’t create, say, an array of 6 billion ints. If they chose 64-bit, some users on 32-bit systems would complain their loops were needlessly slow, and code bloated (why waste 4 bytes of almost every integer in a program?)
Not having generics makes this more of a problem. If go had generics, switching between integer sizes could be a matter of recompilation using a different compiler flag)
A sufficiently advanced compiler would help here, but go doesn’t have one (not exceptional), and doesn’t even aim to have one (compilation speed seems more of a goal than run-time speed)
For comparison, Java's int type is 32 bits on 64 bit systems, but then this limits the array size. It seems like a lesser evil to have a platform-native int type than to limit the size of arrays (especially since servers can be expected to increase their max memory going forward).
Of course, standardizing on 64 bit ints instead would require emulating them on 32 bit systems, when they can't support that much memory anyway (you could argue that 32 bit systems don't matter much anymore, but if you really don't care about ever running on them, then you can just treat 'int' as 64 bits and not variable size).
If you really need to, it's easy to wrap this into your own BigArray-class holding a 32bit-array-of-32bit-arrays and overriding the []-operator with Int64 as argument.
Having said that, I'm by no means a Golang expert – this is the comment of an outsider looking in. I get that the proposer is Rob Pike and obviously Mr. Pike is god-like so what is it I am missing? Is Go 2 meant to break everything? That's like, wow.
Any low-level hardware bit-banging code is going to have to be audited or break in subtle ways, will it not? Is Golang different from C and C++ and languages of that ilk that this sort of change is not that much of a problem? Help me out here folks, I'm genuinely perplexed.
> so what is it I am missing?
The Go team has been adamant about avoiding any changes that break existing code. The whole point of Go 2 is that at some point that might have to happen and they're trying to minimize that pain.
Admittedly, not for things I build in my day-job but mostly for the kind of things you'd get when doing 'project euler' or 'leetcode'. Or more seasonal, the coming "Advent of Code".
As for all the folks claiming they'll leave Go if it gets generics, it's faintly reminiscent of Mac fanboys claiming PowerPC chips were the Very Best right up until this was obviously not true. C++ generics are a PITA in many ways, but you can be insanely productive in the STL without having to hand-roll everything and with good type safety.
Despite the pain, I've been amazed at how easily you can build up some really complex data structures as pretty much one-liners ("Oh, I need a vector of maps from a pair of Foo to a set of Bar") that would either take a preposterous amount of code (or be a type-unsafe disaster waiting to happen) without generics.
Hopefully the final Go 2 generics proposal will capture some of this goodness without some of the horrifying C++ issues (error messages, bloat, sheer brain-numbing complexity).
Baffled at this claim I searched and found one person who really seemed to be saying "if Go changes dramatically...".
This recurring notion that Go fans are anti-generic is not rooted in reality. Instead they simply didn't buy the "either it's there or the language is useless -- generics or bust!" argument that pops up in every Go discussion. It's a fine, if imperfect, language without generics. It's a better language with them.
I suspect a lot of the generics hate is due to a large chunk of the community coming from dynamically typed languages. In which case they're just have a negative reaction to unfamiliarity.
It’s true genetics will shrink some specific APIs, where they are a good fit.
But they will also be used opportunistically by developers excited to push their boundaries.
Of course you can say “just don’t do that” which works if you have a tightly controlled codebase. But most code is not that, and will be handed off to novices over and over for fixes.
So, it’s a question of whether you cater to the advanced developer who can capably handle a vast toolset, or do you commit as a community to more rudimentary tools, in order to reap the rewards of systemic simplicity.
There is no right answer.
I believe in the future all languages will fork into a simpler novice subset for general use and an expansive language for infrastructure. These will both be valid in the same parser, but the subset will be quarantined at the package management level.
Go, EZ-Go, C, EZ-C, etc
I am writing all of my code in EZ-JS
This exactly. The beauty of Go is that currently you do not need a book called "Go, The Good Parts".
Yes, generics would make a lot of things easier, but almost everything more complex and footgunnish.
I don't think we'll see this happen much for existing languages, but it could be a very interesting angle for a newly-designed language (or rather pair of languages).
The focus moves towards a taxonomy of types and developers (myself included) sometimes get stuck on difficult type problems. There's something about trying to preserve type safety which sets the bar extremely high for bypassing the type system when there's not an easy solution and before you know it you've wasted 2 or 3 days writing code which doesn't actually do anything but placate the compiler.
And often that type-safe concoction you create is almost indecipherable when you come back to it later.
For example here's a project I worked on recently which cached a concrete version of a generated method using generics:
https://github.com/DataDog/dd-trace-dotnet/blob/develop/src/...
Used like this:
var originalMethod = DynamicMethodBuilder<Func<object, byte[][], object, object, bool, T>>
.GetOrCreateMethodCallDelegate(
redisNativeClient.GetType(),
"SendReceive",
methodGenericArguments: new[] { typeof(T) });
At least for me that was really hard to figure out how to do and I still have to squint to see what the heck its doing. The non-generic version wasn't type-safe and it wasn't as fast, but it sure was a lot easier to read and understand.And to give some sense of the complexity involved, Rob Pike mentioned in his talk that the proposal spec for adding generics to Go is longer than the spec for the entire language.
I think the complexity is worth it, but I just hope we can be cautious about how and where generics get used in real-world code, otherwise we'll end up with gobbledy-gook that only experts can decipher... and that would be really sad, because the promise of Go was a language normal engineers could be productive with.
They're not all the same though are they.
C++/CLI had both compile-time generics (templates) from C++, and run-time generics from the CLR. And they could be complementary at times.
For compile-time generics, Dlang are a lot more sane that C++, having had the benefit of coming later, and dumping C compatability.
Similarly, CLR (C#, ...) generics had the benefit of being designed having seen Java first, so IIRC they're baked into the CLR. CKR generics were derived from work done at MS Research Cambridge, and I seem to remember Don Syme (F# creator) being rather proud of them. Disclosure: I was contracting at MSR Cambridge back in 2007.
Anyway, the golang designers will be aware of these implementations, so hopefully they'll come up with a nice design.
I don't hate generics. I see the value of generics, especially after having worked with Go for so long; I've had to do some mental gymnastics to get around not having proper generic collections.
That being said, I'm not excited about seeing generics in other people's code. The added complexity doesn't really solve problems I have anymore.
That being said, I'm actually way less excited about overloading.
I think generics and function overloading is going to make me think "where the hell is this coming from?" a lot more often and then I'm going to need to load it up in my IDE, or vim with way too many plugins, and start following definitions.
This seems to be slowly changing, but Go was designed to be a solution to Google problems - python being slow but some c++ applications taking literal hours to compile. Keeping that perspective in mind makes it easier to understand why Go maintainers have not accepted an implementation of generics into the language yet.
Look up things like SFINAE and compile time metaprogramming. Templates were not intended for those things. When they work they're ok, but if they go wrong good luck following the 10 line error message.
Here's an example from Rust:
https://www.reddit.com/r/rust/comments/5nifpm/they_said_rust...
That's what people don't want.
Incautious use of the template mechanism - or just a minor typo - has given me couple screenfuls in the past.
But. There are many modern languages that already have generics. Why can't Go be that one that doesn't cave in and remains powerful in its niche and perhaps may never be the preferred tool in other areas? Why must it be useful for web development, microservices, DAL, etc.?
Any implementation of generics in Go will come with complexity tradeoffs.
I feel like the community learns more about languages and software engineering when we maintain language diversity and see the pros and cons of each approach in practice.
I want generics for selfish reasons, but would also like to see how a modern strongly-typed language solves problems without it.
It's great that they're trying out the process on smaller, more simple proposals. My hope is that this system will either produce really good features, or reveal that there are simply no satisfying solutions.
I still fell things are up in the air about exceptions but maybe we can just agree on generics which would make me feel better.
Using go without generics just felt insane to me...
Things like typeclasses or multimethods offer vastly more abstraction power. These are (a bit) more difficult to understand, but you certainly don't have to be a genius (take it from me). I can kind of get why a language targeted towards "average" programmers might want to omit these.
But here's the thing: The less abstraction power a language has, the more complexity must be handled by the developer. This leads to things like Java Spring, which you do have to be a genius to understand.
This is a fun example since you can do that in Go now, as it comes with a generic vector (essentially) and map :D
I think that was the design decision, 95% of generics use cases are covered by having growable arrays and maps, so why clutter the language with generics.
I like generics, I think most people who dislike them are coming from C++ templates, which has downsides that don't exist in newer generics systems (such as in C# or F#)
Generics allow for less code, thus they are easier to debug, test and reason about. I've used them a lot in C++ and I do miss them in Go. It's not a deal breaker for me either way, but for people who write larger, more complex code, not having them makes it more difficult (more code to write, test and maintain).
C does not have generics either. Go is a lot like C in this regard.
Without generics, if you need a list filled with TPS Reports you basically have 2 choices:
- Use the 'untyped' List that deals with objects. You will have to cast to TPS whenever you look up an item and you will have to make sure that no one accidentally adds a Timesheet to this list.
- Write a custom TpsReportList. I'm sure you'll be able to write an implementation as efficient as the language creators. Oh an if you copy and past from TimesheetList, don't forget to check all names. `tpsReports.AddTimesheet()` is just embarrassing.
With generics, you will have a `List<T>` type. If you need a list of TPS Reports you will use the type `List<TpsReport>`. If you need a list of Timesheets, you will use `List<Timesheet>`.
You can't add the wrong type of item to such a list, you will always get the declared item from that list and you can't assign an instance of one type to a variable of the other type.
There's like three or four different angles, all of which overlap. Some are official. Some aren't. The unofficial ones seem more popular. They're all kind of incomplete in different ways. And it was all such a frustrating migraine to try and figure out. I haven't felt so viscerally aggressive about software like that whole experience made me feel in a long time.
I hope Go2 makes something concrete from the start and sticks with it, for better or worse.
The biggest source of pain moving forward is going to be the projects that haven't transitioned, including the various command-line tools that work on parsing, generating and manipulating Go code (e.g. linters, code generators). Most of the important ones are already there, and I've transitioned several myself.
As an added bonus, word is that the Go team wants an official package repository system (similar to Cargo, RubyGems etc.). I wouldn't be surprised if this happens rather quickly.
It's starting to look like a viable solution, but it's not even close to actually solved yet. Why does `go mod why <module` make changes to your module? How do you run go get to install a remote package when modules are enabled (without explicitly running `GO11MODULE=off go get`)? Why isn't the module cache concurrency safe? Why does the module cache sometimes mysteriously cause compile errors until you `go clean -modcache`? There are so many little bugs and oddities.
And as you mentioned, a lot of things have side effects now that didn't use to, which has catastrophically broken a lot of the tooling surrounding the language. Autocomplete using gocode used to be nearly instant. Now it sometimes triggers downloads and takes 30+ seconds.
I'm hopeful that go 1.12 will be the first release where this problem is really solved.
By the way, your "go get" bug was fixed today [1], if I understand your complaint correctly: With the new modules turned on, you could no longer do "go get" globally.
(I would agree that it's a little weird that "go get" outside a module installs it globally, while inside a module it installs it locally; that's going to trip up scripts and Dockerfiles, and it should really be two separate commands. "go mod add" to add a new dependency, for example.)
[1] https://github.com/golang/go/issues/24250#event-1996119923
I didn't really find that to be the case with Go, so even though Go is a simpler language, I have been more productive, much more quickly, in Rust. Within a couple of hours of starting my project (with only a cursory glance over the docs and tutorial) I had my project daemonized, had options parsing working, had system calls working, got logging working, etc. It was shocking how quickly I was up and running.
I'd been hesitant to try Rust, because it looks big, and I'm kinda tired of big languages. I just don't have enough time/motivation to study a bunch of nuanced syntax and such; I'll never be a great C++ programmer, though I can muddle through and usually understand other people's C++ code. But, so far, Rust is proving to be one of the easier languages I've learned lately, partly due to the holy path being well-defined, and partly due to strong libraries that overlap my particular project perfectly (being a systems language built by very smart people, it has some very good systems libraries).
I don't mean to imply Go is a hard language, it's not. I picked it up pretty quickly, too. Both are easier to learn than, say, JavaScript, because they're much smaller. But, I agree that there isn't a super clear path forward for a beginner with Go, including with installing and building. You just have to acquire more tribal knowledge to work in Go than in some other languages (but, much less than many others...older languages tend to have tons of that kind of thing).
It has excellent package management, robust compiler messages, pattern matching... etc. But once I start trying to build non trivial data structures it becomes a nightmare. For example, doubly linked list, any sort of graph is extremely hard for me to build in rust but extremely easy to build in golang.
I am still learning the full capability of rust, hopefully as I do more practice it gets easier. In the worst case I think I would still use it to build data pipelines since I really enjoyed the syntax and the safety check as long as I don't get into smart pointers or raw pointers.
Also another annoying point is that some libraries use nightly.
[1] https://rcoh.me/posts/rust-linked-list-basically-impossible/
After trying three or four times to build one on their own that is!
- Install Go
- Install VSCode + Go plugin
- Start working
And, yes, I'm finding Rust easier to learn than JavaScript. Without question. It is a much, much, smaller and more cohesive language. It has some new concepts (features or techniques that I have never used in any other language), but so does modern JavaScript, and with JavaScript there's usually five ways to do it, and half of them are really poorly thought out. I don't dislike JavaScript. I'm not saying one shouldn't learn some; one definitely should. But, it's definitely going to be "some", for most people, because there's just too much of it to learn it all, unless you can devote yourself full-time to being a JavaScript expert. I aint got time for that.
As I mentioned, I've built a (toy) project in Go. I've spent more time with it than Rust at this point. I know how it works, and what getting started looks like. And, though Go was pretty easy with few pain points, I've found Rust to be easier and to provide a more clear path for a beginner to follow, so far.
Everyone is different, and we're all coming from different places. No one has exactly the same set of starting conditions for learning a new language as I do. For some, Go may be easier than Rust (I expected it would be, which is why I started with Go and avoided Rust for so long). For me, I am finding Rust easier. I haven't done much with it, but I was surprisingly productive surprisingly fast.
- Install Rust
- Install VSCode + Rust plugin
- Start working
"package" in Go has always referred to a folder. Or in other words, a shared namespace for every entity exported by every file in that folder. "module" refers to a package which has a go.mod file in it; that module package + all of its children become versioned via that go.mod file and are distributed as one unit.
It operates identically to npm; npm packages are folders, and there's a special package with a package.json which versions that folder and all of its children. Then, that "package.json package" is what is distributed.
Haven’t tried it since.
Hope lots of this change to make the language more welcoming for Newcomers to the language.
[1] "... or to ensure that all files used for a build are stored together in a single file tree, 'go mod vendor' creates a directory named vendor in the root directory of the main module and stores there all the packages from dependency modules"
Nowadays I just put my Go source code in <repo>/go/src/example.com/pkgname and that works well enough, but it's a bit clumsy and reminds me of bad experiences navigating Java source trees. I haven’t switched to modules yet but I will once I get 1.12 everywhere.
That's not a feature but a fix for a design problem, and currently the only fix is an experimental one.
For those seeking to invest their time learning a professional tool, that's a whole pile of no-nos that naturally point to a very hard pass.
By your standards c and c++ are not professional tools.
I do wonder why you'd consider Rust, (or even Haskell), to have weird syntax?
Haskell, I feel, needs no explanation.
My experience was similar. In addition to those, I found I really missed a REPL console and moreso something like byebug that RoR has. For those that aren't familiar, byebug lets you put the command "byebug" anywhere in your code that opens an in context REPL. It's enormously helpful for hard to figure out bugs.
The other thing that turned me off from Go was testing. It has good enough support for unit testing but it really lags behind in integration testing. Sometimes you want to know that if you hit this endpoint with this payload you get this response back. It's harder than it should be to write integration testing where you spin up the application and test it end to end.
This is like comparing apples and oranges, or at least, like comparing apples and apple-orange hybrids :)
Just saying that Rails (RoR) has a command like byebug that opens an in-context REPL, seems to ignore the fact that compiled languages like Go catch many errors earlier, at compile time, so you don't even need an in-context REPL or a debugger to find those.
Not saying that features like byebug have no use at all, of course.
It's got more of a learning curve than a REPL, but very powerful. Editor plugins for Go also tend to have good convenience support for delve, making using it less painful. For example, vim-go[1].
[0]: https://github.com/derekparker/delve/blob/master/Documentati...
[1]: https://github.com/fatih/vim-go/blob/master/doc/vim-go.txt#L...
Isn't that what a normal debugger does? Or am I simply just living in lala-land because of C#.NET/Visual Studios terrific debugging experience?
I remember php and the hell that was xdebug. It was much easier and more efficient to simply to a `var_dump` whenever you needed to debug.
I strongly dislike the non-standard internals (horrendous custom assembler, direct usage of syscalls instead of libc) and the "developers are too stupid to use this" attitude towards modern language features.
But go isn’t built on top of C? Why would they add more dependencies that complicate and slow down the compilation process and hurt portability?
$ cg gh:torvalds/linux # cg = cd to git repo
$ pwd
.../src/github.com/torvalds/linux
The code is at https://github.com/majewsky/gofu#rtree if anyone's interested.Why do you want to use libc for Go? You wouldn't use the Rust standard library for Go, why the C standard library?
MacOS doesn't guarantee backward compatibility for direct syscalls. This has caused bugs like this with compiled Go binaries:
https://github.com/golang/go/issues/16570
Go has very recently started using libSystem (which is analogous to Linux libc or Windows CRT) on macOS to avoid this issue.
On a more philosophical level, POSIX is defined in terms of a C standard library, and not using libc means Go doesn't support and/or must implement itself various features that are otherwise provided to POSIX applications by the system (like locale handling). Your mileage may vary in terms of whether that's a bad thing or a good thing.
EDIT: Removed a parenthetical that was based on a misreading of the parent.
the performance, the community around it, and vendors support
Go is pretty easy to get up and running in Windows. There's an installer for the compiler and you can install vscode and the Go extension pretty quickly.
Windows is an afterthought for most programming languages (ever try ruby or c++?) and Go's cross-platform capabilities were a breath of fresh air.
Regular grammars are great for parsers. But really, having an easy to read (conceptually!) language is way more important imho.
But call me crazy when I say that I like C++ and can read it effortlessly :)
It just takes time, you don't want to spend that time getting used to Go and that's perfectly understandable.
So it's fair to say Go's syntax is unfortunately limited in expressiveness (and I too find myself mystified by how to express ideas neatly with go), but I have a hard time imagining C++ is something worth emulating without adding a whole bunch of subjective caveats to what "should" be avoided.
It reads more left to right then right left like c++.
That said, any major additive change to Go, especially generics and/or try/catch will push me away from the language. If I need a well designed language, I have Rust. Go's sell for me is it's so naively simplistic it's actually useful when your team members are idiots.
If they bolt on type variables, well that's just a different language, and we'll just end up on the hedonistic treadmill towards another Java. No thank you.
My team has ~200k lines of Go code and I'm exceedingly happy with the state of the codebase. Having previously maintained a C++ codebase of similar size, I can say that the pace of changes is higher, the effort necessary for large scale refactorings is lower, and our ability to reason about the system is similar.
A few examples:
- Refactorings are simpler due to the use of consumer-side interfaces. Say you want to inject an in-memory cache above a backing store. To do that you probably have to change the constructor and provide an implementation matching the 2-4 relevant methods. That's it.
- Tracing code is slightly worse, due to having to track callers through said duck-typed interfaces, but on the flip side multi-threaded code is sufficiently simpler to reason about that I call it a wash. Having previously had to do threading in the form of "control flow" state machines, and then fibers (which were better but not perfect, and still aren't widely available), Go constructs are great. Locks where appropriate, channels where appropriate, overall very fast and clean code.
- Performance is good, and reliable. Not as good by cycle-count as C++ - and e.g. the comparable RPC libraries are definitely less mature than Google's very-well-kicked C++ libraries - but on the other hand it scales almost linearly. We started a system at ~5 cores/task under AutoPilot, and then when we next got around to adding more tasks it was peaking at ~60 cores/task at essentially the same per-core throughput. I've never managed to write a C++ server that can accidentally scale concurrency by >10x without hitting _some_ bottleneck.
- We use Go for ~everything. Server code, definitely Go. Client tools, also Go. Simple scripts, bash until they need their first 'if' or flag or loop, then Go too.
- I'd prefer real generics to interface{}, but the number of places it comes up is minimal enough that it's no more than a minor annoyance.
I can't speak to the issues of package management - we dropped compatibility with Go's native package structure fairly early on and went all in with blaze/Bazel (http://bazel.io) to coordinate builds and dependencies and whatnot, and haven't had reason to try modules yet.
If you don't mind, can you give a little insight on what this looks like in practice? I'm not sure how to use a compiled language as a script. I've played with executing go as a script using a shebang hack, but I somehow don't think this is how others are doing it.
For reference, the shebang hack I was using looked like this:
//usr/bin/env go run $0 $@; exit $?
package main
import "fmt"
func main() {
fmt.Println("i am a script")
}Naturally those of us coding since the early days have experience with programming languages without generics support, yet a large majority eventually adopted generics.
Even C has minimal generics support since C11.
Personally, in any typed language, I want generics, not having them feels very limiting.
I do think that generics are needed if go wants to become more useful in contexts others than network middleware or data plumbing, but i'm also pretty sure that adding them will help cripple a lot of codebase in a very short term.
-- Rob Pike
From https://channel9.msdn.com/Events/Lang-NEXT/Lang-NEXT-2014/Fr...
And there are other hints on https://talks.golang.org/2012/splash.article
Just remember, they probably think the same thing.
The problem with context isn't necessarily the interface, it is that it is "viral". If you need context somewhere along a call chain, it infects more than just the place you need it — you almost always have to add it upwards (so the needed site gets the right context) and downwards (if you want to support cancellation/timeout, which is usually the point of introducing a context).
Context's virality also applies to backwards compatibility. There have been discussions of adding context to io.Reader and io.Writer, for example, but there's no elegant way to retrofit them without creating new interfaces that support a context argument. This problem applies to any API; you may not expect your API to require a context today, but it might need one tomorrow, which would require a breaking API change. Given that it's impossible to predict, you might want to pre-emptively add context as an argument to all public APIs, just to be safe. Not good design.
Cancellation/timeout is arguably so core to the language that it should be an implicit part of the runtime, just like goroutines are. It would be trivial for the runtime to associate a context with a goroutine, and have functions for getting the "current" context at any given time. (Erlang got this right, by allowing processes to be outright killed, but it's probably too late to redesign Go to allow that.)
(I'm ignoring the key/value system that comes with the Context interface, because I think it's less core. It certainly seems less used than the other mechanisms. For example, Kubernetes, one of the largest Go codebases, doesn't use it.)
Doc: https://godoc.org/github.com/dolmen-go/contextio Example: https://godoc.org/github.com/dolmen-go/contextio#example-pac...
However, the reason we don’t use that more is partially because of the viral nature of context - it came late in kube lifecycle, so we didn’t ensure it everywhere and now it’s a lot harder to wire (clients have been iterated on for a while).
I have a closed PR from 2015 to kube that added per request ID tracking that we closed as “wait until we have context everywhere” and we’re still waiting.
Go Context Scoping [1] by Eyal Posener. It even has a working PoC implementation [2] which is pretty ergonomic and could become even moreso with language integration.
I think this concept solves most of the problems we currently face with context. As a consequence of making context available per goroutine it basically becomes an implementation of gouroutine-local storage. But this is more because context.WithValues exists in the first place than because of this proposal. In fact context has effectively become the de-facto GLS anyway, except it makes everyone's code ugly to do it.
> After almost 10 years of exposure, we have learned a lot about the language and libraries that we didn’t know in the beginning, and that was only possible through feedback from the Go community.
It's so tempting to hold one's project back until it seems perfect. And then, even worse, to defend it as perfect in the face of real-world feedback. I really appreciate it when smart people do their best, but in full recognition that a lot of things will be learned once real use happens.
So at least this first round is quite unlikely to break anything in real use.
Also, I prefer using ' for the thousands separator and was happy when C++ adopted it. It's less visually intrusive, especially with variable width fonts, and some calculators even use apostrophe. Also, in identifiers, the underscore has semantic meaning: foo_bar is different from foobar. But 1'234'567 is meant to be identical to 1234567, so underscore isn't the best choice.
> #19113 Permit signed integers as shift counts: An estimated 38% of all non-constant shifts require an (artificial) uint conversion (see the issue for a more detailed break-down). This proposal will clean up a lot of code, get shift expressions better in sync with index expressions and the built-in functions cap and len. It will mostly have a positive impact on code. The implementation is well understood.
The proposal as far as I can make out says allow signed integers for shifts but panic if they are negative.
This seems like a step backwards to me pushing checking which the compiler made you do to runtime.
Personally I'd expect a negative shift to shift the other way, but that doesn't seem to be a popular option with the team.
No that means this operation is currently unchecked by the compiler: https://play.golang.org/p/nJmaEOkObk1
Please no ...
I'm also worried about how being "community driven" will change the philosophy of the language.
But I don't think that they're going to let the community run wild and completely change the character of Go. I think they're looking for wins that matter to users, but wins that are possible within the framework of what Go is.
When I read commentary about suggestions for where C should go,
I often think back and give thanks that it wasn't developed
under the advice of a worldwide crowd. C is peculiar in a lot
of ways, but it, like many other successful things, has a
certain unity of approach that stems from development in a
small group.That said, it's mostly just half, before it was like 3/4 or worse. It has improved in stability (I haven't had it randomly disconnect at all since late Go 1.9 days, but I haven't pressed it hard at all either) and GoLand's integration works well and is mostly fast. It just can't do anything except set breakpoints and view memory.
[1]: https://github.com/derekparker/delve/blob/master/Documentati...
I use GoLand for development, which I believe uses Delve by default and it's entirely pleasant.
I really want to utilize those 2~4K stack spaces for the routines.
Are there really "millions" (plural) of Go programmers?
Sounds like a bit of an overestimate, no?
Note that these were the first two Google search results I found for "number of programmers in the world" and "percentage of programmers in different languages" so... being off by a factor of 10 or more is likely.
[1] https://www.computerworld.com/article/2483690/it-careers/ind... [2] https://www.statista.com/statistics/793628/worldwide-develop...
Think about it: the number of programmers in the world is certainly not off by 10 factor - no way there are 180 million programmers in the world, only 18 of which are visible.
Similarly, there's no way that 43% of programmers in the world are Go programmers, that's again quite clearly off.
I think an estimate of 700k Go programmers makes a lot of sense, maybe with a factor of x2. I would very strongly doubt that there are more than 2 million Go programmers in the world.
I remember something about generics not being supported.
These are Go2 proposal PRs, sorted by upvotes.
What does the hivemind think?
What projects did you have in mind? I think the answer to your question depends on what you mean by "systems level programming" and what you plan on doing/trying.
"As a rule of thumb, we should aim to help at least ten times as many developers as we hurt with a given change" sounds like there might be breaking changes, but on the other hand Robert still talks about including new features in the Go 1 compatibility guarantee.
I'd love if the compiler would stay backwards compatible and packages / modules could be pinned to a certain version, either during import or in the package / module itself. Then one could write Go 2 code but still use packages which are not yet updated to Go 2. Personally I think that making breaking changes is a good idea, as it allows to clean up previous mistakes. However, Go should at all cost avoid incompatibilities like between Python 2 and 3.
int (*f)(int x);
The variable type is around the variable name. Some of it is before and some of it is after. In Go it’s simpler: var f func(int x) int
If I want a void function, I can just leave the return type off, I don’t need to add "void": var f func(int x)
It’s easier to write a parser for this, because you know that this is a variable declaration just by looking at the very first token and finding a certain keyword. If you write a parser for C or C++ it’s much more complicated, because you have to keep track of which identifiers name types in the scope that you’re in. Generally, more modern languages like Java, C#, Go, and Rust are much easier to parse, they are often designed to be relatively straightforward to parse with e.g. an LL(1) parser, or close to it, maybe you can just use recursive descent.In C++ it's also a bit inconsistent,
int f1(int x) { return x + 5; }
std::function<int(int)> f2;
auto f3 = [](int x) -> int { return x + 5 };
You also have to invent a placeholder for when there aren’t types: int x = 3;
auto x = 3;
In Go you just omit the type, and you don’t need a placeholder: var x int = 3
var x = 3
Other languages where the type comes after: Haskell, Python (PEP 484), ML, Rust, Pony, Nim, TypeScript, Swift.In fact I think the way C/C++/C#/Java do it, with the type at the beginning, is actually somewhat rare.
procedure SetColor
(const NewColor: TColor; const NewFPColor: TFPColor); virtual;
Rust does it too: fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32
Go is similar, but less symbols: f func(func(int,int) int, int) func(int, int) intYou get used to it. Having to deal with this kind of differences between different programming languages is a lesser concern in the grand scheme of things.
Swift, Kotlin, TypeScript, Rust
Like everything different, it can feel odd at first, but I actually prefer it now. Think of it as "Joe is a Person" is more natural than "Person named Joe".
Love Go's one binary does http-server-login-everything-etc, can't be simpler for deployments.
func(foo, bar int) bool
I was hoping "check" would make the cut.
https://homepages.cwi.nl/~storm/teaching/reader/Dijkstra68.p...
I will say GOTO's work well for exception handerling, but still doable without. But for speed, though less of a factor today, it still translates as faster code at the low level of CPU runs.
But then, things move on and you will always have that wave of what students are taught being cutting edge compared to what is actualy in use in work. A friction many would have encountered in one form or another, even today. Be it style, design or language/tools choice. What is the best today, may well be outdated tomorrow, but you have to maintain that legacy investment and it is often too costly/risky to rewrite that legacy for something that itself could be legacy the next day.
So as for harmful, well they can spagetti up code if used badly, yet that comes to many approaches and if they are that bad, why do CPU's still have JUMP instructions you could counter-argue.
Oh and well played on the humour.
Here's the equivalent Reddit thread; better suited for this sort of comment:
https://www.reddit.com/r/golang/comments/a1j3h6/go_2_here_we...
this is the new Reddit
So, do you do functional programming, and is Rust a better (with all the subjectivity that word implies) language than Go?
I would love to have ML/Scala-style syntax for curried functions and function definition via pattern-matching with guards, which is also, it seems, out of questions.
Actuall, the more of ML a strict language gets in - the better.
What is really funny is that Bell Labs did a lot of ML research, especially on stdlib, but Go team is ignoring everything which is not of the C flavour. Pity.
Again, ML is absolutely wonderful, and type-classes are the biggest single major innovation since Smalltalk.
It is better to lean towards ML instead of heading towards Javascript.
"Use of Go 2 Considered Harmful".
https://docs.google.com/presentation/d/1DmyTABhGLvN0m2uHktvk... (slide 140)
Ian had a good talk & doc about this too recently:
https://github.com/golang/proposal/blob/master/design/28221-...
I loathe working in Go.
I'm all for full support for unicode string manipulation.
But when ever are "unicode identifiers" a good idea? All kinds of BS decisions (normalization etc) for no good reason at all. Would you share code with identifiers written in RTL language? Chinese? Hieroglyphics?
And I'm saying this as someone who's not a native English speaker.
If it was an APL dialect I'd see some reasoning, but what good does it do for Go?