11 ms·
Go says Wat
- speps 8y agoWAT 16 is the most shocking really. Reminds me of Python 2 where True and False can be overridden as well, it was fixed in Python 3.
- sacado2 8y agoWho does `true := false` in actual code, though? However, in real life, I've already wondered if using `new` as a variable is bad practice or not: old := x new := foo(&x) if old != new { return &Bar{} // oops, return new(Bar) won't compile } That should at least be caught by linters.
- speps 8y agoWATs are usually never real code, some of the ones in there are but I would encourage you to look at the linked video in the article for a fun look at other languages WATs: https://www.destroyallsoftware.com/talks/wat https://www.destroyallsoftware.com/talks/wat
- sacado2 8y agoSome of those in the original presentation can actually happen accidentally because of dynamic typing, though. For example, you could get a value from a JSON object, expect it to be a number, but have objects instead, and perform a {} + {} without actually knowing it, have the interpreter execute it with no error, and wonder where in your code you caught that NaN value (because it would propagate all along). OTOH, it's very unlikely you type accidentally `true := x` in your code, and even if you did, that would very probably be caught by the compiler anyway.
- coldtea 8y ago>Who does `true := false` in actual code, though? Anybody who is still human and can do a typo.
- sacado2 8y agoWhat typo can that be though? If you wanted to do `if true == x` and typed `if true := x`, the compiler will tell you. If you mistyped your variable name, and wanted to do `tue := false` for instance, but inadvertantly typed `true := false`, the compiler will tell you "true declared and not used". I can't really find a compelling example.
- coldtea 8y ago>If you mistyped your variable name, and wanted to do `tue := false` for instance, but inadvertantly typed `true := false` You don't need to have a variable like "tue" to make such a typo. Since your writing an if statement, you already have "true" and "false" in mind, so you just need to be a little absentminded and voila, instead of foo := false you've written true := false. Plus, lots of naive editors will offer to autocomplete something starting with t to true (if they find the token true used elsewhere within the file). >the compiler will tell you "true declared and not used". Only if you don't use it. But a few lines below you could very well be using true legitimately too, in which case it wont tell you: true := false ... if condition == true { // ... }
- sacado2 8y ago> Only if you don't use it. But a few lines below you could very well be using true legitimately too, in which case it wont tell you: Good point! Edit: but then, your expected original variable name, `tue` in my example, would be used while not yet defined: true := false if foo() || bar { tue = true // Compilation error } ... if condition() == true { ... } Only possible case I can think of: you already defined tue, but tried to redefine it via variable shadowing: tue := true ... if cond() { true := false if foo() || bar() { tue = true // Will compile, but not the tue you expected } ... if condition == true { ... } }
- SamWhited 8y agoThe "it's 3AM and I want to write 'myvar := true' and am thinking about 'true' so I write 'true := true' and move on and don't notice" typo.
- jonbodner 8y agoI chose true := false for the humor and shock value. As you point out, accidentally redefining the meaning of len or new or close is far more likely.
- sacado2 8y agoI'm with you there, I have to admit I already did the true := false trick for the amusement and bewilderment of my colleagues.
- Groxx 8y agotbh I think it's a lot less of an issue. It's creating a new var named `true` (or `false`) - that only exists in the scope it's declared in, not globally. I think it's crazy that this isn't a compile-time error, but it's not even close to "you can change the global value of builtin values".
- krylon 8y agoOn the one hand, I find it revolting that the language will let me do that. On the other hand, why would I ever want to do that? It's not something one might do by accident. It's along the line of C letting you say idx[arr] instead of arr[idx] It's unfortunate that it is possible, but there is no good reason to ever do this unless one is trying to confuse one's enemies.
- hajile 8y agoJS used to do something similar with being able to redefine undefined.
- cdoxsey 8y agoThis was a design chose to minimize the number of keywords in the language. > The rationale is pretty simple: Only identifiers that must be keywords for syntactic reasons are keywords. > In fact, in the very beginning there was some discussion as to whether things like nil, iota, etc. should be keywords. Eventually we agreed on the rule above which settled it. https://github.com/golang/go/issues/18193#issuecomment-264921280 https://github.com/golang/go/issues/18193#issuecomment-26492...
- IshKebab 8y agoExcept for the first two I don't these are any where near as bad as the original Wat ones. Still, nice list of small gotchas.
- sacado2 8y agoHmm, the first few WATs are pretty bad examples. Any developer, gopher or not, should know that, when you reassign the value of a function parameter, the variable when the function returns still has its original value. It's pass-by-reference 101. Now, sure, slices and the way append work are a bit complicated to grasp at first, especially if you never used C, but this is not a WAT. A lot of WATs are more bad practice than problems in the language, IMO (like WAT 8, modifying the return value twice in deferred functions, with the order being significant: who the hell writes such a code?) A few ones, though, really are problematic. For instance I've been bitten more than once by variable shadowing, especially with error values. IMO, this is the weakest point of Go.
- masklinn 8y ago> Now, sure, slices and the way append work are a bit complicated to grasp at first slices are not complicated, the issue is append, mutable slices and backing arrays being shared, that's what's really fucky about it. And that interaction is absolutely a WAT. Especially the ability to separately append to two slices with the same backing array. > A lot of WATs are more bad practice than problems in the language, IMO Much the same could be said about the original presentation. There absolutely are non-WAT sections in that article though. (4) is one.
- kodablah 8y ago> And that interaction is absolutely a WAT I'm not sure views into a fixed-sized array is really confusing/strange at all. Whether you create multiple views in the array, mutate the views, etc. All interactions that occur on slices and arrays make plenty of sense. How you'd write something like append yourself if "Slice" was your type w/ a backing array, a start index, and a length is quite straightforward.
- TheDong 8y ago> Whether you create multiple views in the array, mutate the views Whether there are multiple views or not is actually an implementation detail, and whether "append" chooses to allocate memory or not will change whether there are multiple copies or multiple views of the array. All of that behavior is something you absolutely cannot rely on.
- ainar-g 8y agoWAT 3 and WAT 10 were surprising for me. WAT 3 becomes less surprising when you consider that a method on a pointer can be called and can return a valid result even if the pointer is nil. WAT 10 just seems inconsistent. I said this before, and I'll say it again: shadowing does more harm than good. The rest are pretty trivial for anyone who worked with the language for over a year or read the documentation carefully.
- blixt 8y agoI was really expecting the slice WATs to carry on to talk about what I consider a real WAT: you have to perform robust checks on your input slice's capacity and length if you use append in a function, because append makes its own choice whether the array of a slice is reused or copied. Here's a basic demonstration: https://play.golang.org/p/8zMmPwtjcxG https://play.golang.org/p/8zMmPwtjcxG In particular, note how this behavior can easily be forgotten when the semantics are hidden through variadic arguments that are passed an existing slice (instead of the automatically created one when you pass in actual variadic arguments).
- masklinn 8y ago> I was really expecting the slice WATs to carry on to talk about what I consider a real WAT: you have to perform robust checks on your input slice's capacity and length if you use append in a function, because append makes its own choice whether the array of a slice is reused or copied. At a fundamental level, the issue is that Go's design decisions make any modification of a shared slice dangerous, and the language provides no way to mitigate it. A really fun one is divergent appends to slices with a shared backing array and enough capacity for an in-place append: https://play.golang.org/p/ZHWo3bFOR0X https://play.golang.org/p/ZHWo3bFOR0X
- blixt 8y agoThat's the one I show in my example above :) The final example shows how paired with variadic arguments very "weird" things can happen since your slice may come from an entirely different codebase and you may get very difficult to debug results.
- Groxx 8y agoNils are among the biggest wats in Go in my experience. "useful nils" just compounds "the billion dollar mistake" into something even more nefarious and even harder to identify during code review. Along similar lines: WAT 15. Under what circumstances do you expect a nil var return to become non-nil? Did you even know that was possible?
- masklinn 8y ago> Along similar lines: WAT 15. Under what circumstances do you expect a nil var return to become non-nil? Did you even know that was possible? Yeah. Typed nils are a horrible, horrible feature: when you cast a value to an interface, it creates a fat pointer of (Type, Value). From a nil, that's (nil, nil) but from a nil Foo that's (Foo, nil). And since `==` just does a straight value comparison, (nil, nil) != (Foo, nil). This actually has an official FAQ entry telling you to go fuck yourself: https://golang.org/doc/faq#nil_error https://golang.org/doc/faq#nil_error
- seandougall 8y agoI really enjoyed Go until I started trying to work with interfaces. The fact that you have to understand the implementation details at this level in order to use them reliably, IMO, shows that when the Go team talks about language simplicity, what they really mean is _compiler_ simplicity. At some point, the complexity starts getting offloaded onto the end developer. That said, the fact that the crowd only offered an incorrect guess four times out of 16 is telling. This really didn't have the same feel to me as the original WAT talk, which is really full of truly strange things.
- int_19h 8y agoIMO, Go is an attempt to design a modern language using C philosophy, and it shows. Like, this whole thing about slices is because they insist on trying to make arrays + slices into a data structure that does everything "good enough", while mostly retaining their performance profile. And so you end up with all of those abstractions that look kinda sorta like what you'd expect in a more typical modern language - but the moment you start poking hard at them, they leak like a sieve.
- stcredzero 8y agonil is weird in Go, and in most languages with an equivalent concept. It's a "hole in the type system" In Smalltalk, nil just contains the sole instance of the UndefinedObject class. It's not a hole in the type system. Instead, it becomes a paradox in the inheritance system, because it's used as a superclass. Here's a WAT. Smalltalk is actually strongly typed. It's just that everything has the same type of Object. (The type system has no holes. However, it's the size of a thimble!)
- jonbodner 8y agoHi, I'm the presenter AMA. One general comment; not every question was intended as a WAT. Some were setups to introduce a WAT.
- stcredzero 8y agoAt first I was wondering, what is this? Anyone with a Comp Sci degree who read the docs should already know most of this! Then I thought, "This is one of the best stealth Go basic Comp Sci education presentations I've seen in awhile!"
- rapidloop 8y ago"The default type (used for inference) for an int constant is int, which is a 32-bit type" -- this is not true, the size of int is platform-defined. See https://golang.org/ref/spec#Numeric_types https://golang.org/ref/spec#Numeric_types. The error you're seeing is because on 32-bit platforms where int is 32 bits, 2^64-1 does not fit an int, and on 64-bit platforms where int is 64 bits, 2^64-1 does not fit an int either (int's are signed). This will work on 64-bit platforms though: a := math.MaxInt64 fmt.Println(a)
- jonbodner 8y agoUnfortunately, the transcription doesn't match what I said. The video is now available at https://youtu.be/zPd0Cxzsslk https://youtu.be/zPd0Cxzsslk
- krylon 8y ago"The order of iteration for a Go map (a hashmap) is unspecified, by design." I that surprising behavior? It is not only well-documented, but is in line with most other languages that offer a hash table / map / dictionary / whatever in the base language or standard library.
- miranda_rights 8y agoThe Go runtime explicitly randomizes the iteration of map, because the language designers noticed 'Programmers had begun to rely on the stable iteration order'[0]. Most languages, as far as I can tell, tell you that the iteration order is undefined but frequently have some stable iteration order that's consistent for the compiler or the system architecture, and don't take the extra step of randomization. [0]https://blog.golang.org/go-maps-in-action https://blog.golang.org/go-maps-in-action - Header: Iteration Order
- krylon 8y agoThanks for the clarification! When I first learned about hash tables (in Perl), the book I used said that the iteration order over a hash should never ever be relied upon, and that it could change arbitrarily from one release to the next. If the order mattered, one should use a different approach (get the keys and sort them by whatever criteria), plain and simple. I guess that has sunk in pretty deeply with me. ;-) The .Net framework provides an OrderedDictionary and a SortedDictionary, both of which make some kind of promise regarding iteration order, but I have never used them myself.
- wwweston 8y agoHaving done the work of observing langugage users apparently want an ordered map, were there any steps taken to add such a facility, or did efforts stop at letting developers know they were they were wrong by taking away a de facto feature?
- likpok 8y agoOrdered maps require other tradeoffs. For example, c++ has both: std::map and std::unordered_map. std::map is a treemap, and so has logn access/insertion times. It also does a ton of small allocations, and scatters your data across memory, leading to poor cache performance at scale. But it has ordered output, and doesn't invalidate iterators on insertion (hashmaps might, because they sometimes need to rehash). Generally, the suggestion that I've heard is to avoid using std::map unless you really really what it specifically provides, because it's expensive and it's hard to know if you can safely relax those constraints.
- leshow 8y ago> Go avoids magic What? Go is all about magic. All the std lib stuff that is able to take any type works by magic. Those "obscure rules" (which I prefer to call being able to reason about code) look like they are going to get added in Go 2. It's odd how programmers think polymorphism is this obscure thing when it's available in almost every typed language.
- seandougall 8y agoI would add garbage collection to that. Being able to ignore memory management and trust it to be handled at a non-deterministic time by a separate entity outside of your control is certainly useful, but it's also about as "magic" as programming gets at the language level.
- deleted 8y ago[deleted]
- kbd 8y agoI didn't finish reading all the WATs, but if these are the best WATs about Go, it must be a very consistent language. The first two, for example, aren't WATs at all. The Go book is very clear that you need to use the result of `append` to get the modified slice. WAT 4, map traversal is unordered, is not at all a WAT. WAT 7, maps are reference types, is also not surprising at all. WAT 8 is also not a WAT, defers are defined to be processed in LIFO order, and the rest falls out of how named returns work. This was a waste of time :(
- lostmyoldone 8y agoI think that what you are saying is true, in a manner. To my eyes, it is however in the manner that for the small syntax and the small number of idioms, it consistently surpises me how weirdly they interact.
- kbd 8y ago> it consistently surpises me how weirdly they interact. What exactly surprises you? I just read the Go book and none of what I saw was surprising in the least. I consider a WAT something like Python mutable default arguments that are certain to surprise every Python programmer at some point.
- songgao 8y agoThis old blog post has a pretty good explanation about how Go slice works: https://blog.golang.org/go-slices-usage-and-internals https://blog.golang.org/go-slices-usage-and-internals Once you realize slices are just (pointer, length, capacity) structures, and the structure itself is copied by value, the first 2 WAT is pretty trivial.
- erik_seaberg 8y agoIt's kind of broken that slices are immutable but maps aren't, and that reading a slice can panic but reading a map can't.
- SamWhited 8y agoReading from a nil slice and map feel consistent to me. If you read from a nil map you get the same result as reading a key that is not in a non-nil map (because that key is clearly not in the map because it's nil). Similarly, if you read from a nil slice you get the same result as reading an index that's not in the slice, an out of bounds access.
- lifthrasiir 8y agoTrivial in isolation, but it is much harder to see the problem in a heap of source code. In general such "surprise moments" should be regarded seriously however it seems trivial in isolation.
- unit_circle 8y agoWhy does this have so many upvotes?
- schmichael 8y agoI would add WAT 2.5: https://play.golang.org/p/gwcHh4-QhHK https://play.golang.org/p/gwcHh4-QhHK func main() { x := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} fmt.Println("orig: ", x) mutate(x) fmt.Println("append: ", x) mutate(x[:4]) fmt.Println("sliced: ", x) } Appending to the end of the slice creates a new slice and therefore the caller doesn't see the mutation. However appending to a slice-of-the-slice leaves capacity in mutate's copy-of-the-slice to append without allocating a new backing array. Therefore mutate happy bumps the len of its slice copy and mutates the callers slice! This bit me once in real production code when reusing a []byte array to avoid allocations. The bug was obviously my fault, but this behavior can be easy to inadvertently trigger if you're trying to avoid allocations! Edit: fixed code formatting. It's 2018 YC, please please please implement at least a subset of markdown.
- howeyc 8y agoIndeed, that's why one of the go versions (I don't remember which) added the capacity as the third argument when "slicing." https://play.golang.org/p/FCm8nOElz2n https://play.golang.org/p/FCm8nOElz2n
- caffeinewriter 8y agoI feel like every language should have at least one "wat" style talk or article. Even for languages you adore, it's important to understand the quirks, shortcomings, and issues in a context that might seem absurd to an outside developer.
- mikelward 8y agoI feel like WAT3 is the only real WAT. Looking up a key in a map you didn't "make" gives you the zero value. I think that's consistent with much of Go. But if appending to a nil slice works, assigning to a key should never panic, even if you didn't "make" it first.
- nemo1618 8y agoYou call that a WAT? I'll show you a WAT. https://play.golang.org/p/ePRpDbaFaRl https://play.golang.org/p/ePRpDbaFaRl
- Buge 8y agoIt is confusing, but at least the io.Writer documentation calls it out as bad code https://golang.org/pkg/io/#Writer https://golang.org/pkg/io/#Writer >Implementations must not retain p.
- bradknowles 8y agoSo, for those of us who are not GO-fers, what is a WAT?
- int_19h 8y agoIt's not a Go-specific thing. I think this presentation started it: https://www.destroyallsoftware.com/talks/wat https://www.destroyallsoftware.com/talks/wat
- cliffordthedog 8y agoNumber 4 isn't really a wat, it's just a design choice. other languages offer maps that do retain order.
- Thorrez 8y agoNow combine that with the extremely confusing net.IP and net.IP.To16() and you can get a sneaky bug that I've seen in practice. https://play.golang.org/p/SGgR4RSCPvM https://play.golang.org/p/SGgR4RSCPvM
- kc1116 8y agoFeels like a lot of work went into coming up with these WATS
- jonbodner 8y agoThe video for the talk is now available: https://youtu.be/zPd0Cxzsslk https://youtu.be/zPd0Cxzsslk