3 ms·
Go still has some unecessary sources of complexity, for example: 1. It uses null (the billion-dollar mistake) to indicate optional values. A better designed la
by jephir 12y ago
Go still has some unecessary sources of complexity, for example:
1. It uses null (the billion-dollar mistake) to indicate optional values. A better designed language uses an option type.
2. It has conflicting idioms for conditions. Error types use the `if err != nil` idiom while map access uses the `if ok` idiom. This means that you read the main execution path downwards and the error path to the right, unless you access a map, then the execution path goes to the right and the error path goes downwards.
3. The `:=` operator declares new variables, unless you use it to declare multiple variables where one already exists in the same scope. As a result, you don't know if you have actually declared a new variable using `:=` unless you read upwards to see if a declaration for the same variable name already exists.
- nkozyra 12y ago#3 has always bothered me. I feel like: someString := "hello" someString, err := ReturnStringAndError("world") if err != nil { fmt.Println("there's no way this could happen") } else { fmt.Println(someString) } Should produce an error.
- nhaehnle 12y agoIt's a matter of pragmatism. When you have a sequence of functions that all return (res, err) pairs, it's extremely helpful to be able to use := even though the err is not redefined. Occasionally, I wonder whether := with several variables on the left hand side should have been to defined to redefine variables (shadowing the earlier definition), but obviously the Go people thought that such shadowing would be worse.
- NateDad 12y agoYup, this is why you don't need err1 err2 err3 err4... (I've had to do similar things in C# before). Also, := does shadow in a sub-scope. The difference between shadowing and simply assigning in the same scope is negligible (i.e. either way you can't get at the old value). err := foo() if err != nil { err := bar() // err shadowed here } f, err := baz() // err assigned here What would be the difference between shadowing and assigning on that last line? You can't unshadow without leaving scope, at which point the value you were shadowing also leaves scope.
- jnks 12y agoThe solution to number 2 (if it really needs one) is simple to use `if !ok` instead to push the exceptional case to the right.
- grey-area 12y ago3 very good (if minor) points here - it'd be great if the go team looked at fixing things like this for a go 2. I've heard talk they don't have any ideas of what might be in a go 2, but a few grammar tweaks like these plus pkg dependencies would be a good place to start. There are definitely some rough edges to the language and some decisions which in retrospect they might not have made, but since it's frozen at go 1, there won't be any changes for the foreseeable future. In some ways that's comforting if you're building real apps on the language, so not at all a bad thing. I'm not even sure if they need a := operator, is the additional complexity and wasted time worth the benefit (compile error on redeclared variables, most of the time)? It feels a chore having to type out := most of the time and, as you point out, confusing where there are several variables assigned at once. Another area which is unnecessarily complex is allocation: http://golang.org/doc/effective_go.html#allocation_new http://golang.org/doc/effective_go.html#allocation_new You might use: func () (t T) {} var t T t = T{} t := &T{} t := new(T) t := pkg.New() t := make([]T,5) Though the pkg.New() option is really just an optional convention. It all makes sense eventually, but do we really need all of these options? I'd rather have something like: t = T{} t = &T{} t = [5]T Still, these are pretty minor niggles, which can mostly be overcome with conventions, except perhaps the use of nil everywhere, which is a shame. There's a lot to like, and the emphasis on simplicity (at the cost of features) definitely is a feature of go, even if it does have some rough edges it is significantly simpler than most other languages both to learn and to use.
- xkarga00 12y agoRob Pike mentioned variable declaration when asked at GopherCon what he wished he hadn't done in Go http://youtu.be/u-kkf76TDHE?t=16m10s http://youtu.be/u-kkf76TDHE?t=16m10s
- jhawk28 12y agoI don't see #1 as a problem. Only pointers can be null, values cannot. Strings are not null terminated so that removes a whole class of problems.
- alecbenzer 12y ago1. Mistake or not, allowing all pointer types to be `nil` seems like simplification from things like option types. How do you see nil as adding complexity? 2. I've never heard of `if ok` being an idiom _as opposed_ to `if !ok`.
- tikhonj 12y agoNil adds complexity because it creates a hidden failure condition in every possible value. One that's not necessary most of the time. Option types, on the other hand, are not a source of complexity, because they just use a more general language feature (ie variants). And variants aren't a source of significant complexity because they're symmetric to records (ie structs) and so emerge naturally. Symmetry inherently simplifies design by organizing and structuring things. They're a very small step beyond enums and a much saner alternative to unions. Nils are ultimately more complex because they're baked into the language and omnipresent. And they give you very little in return! Variants, on the other hand, are a natural and relatively simple design that also vastly increases the expressive power of the language. And gives you option types for free.
- burntsushi 12y ago> Nil adds complexity because it creates a hidden failure condition in every possible value. That's simply not true in Go. `nil` is not an inhabitant of all types. Integers, floats, strings, arrays and structs cannot be `nil`. Slices, maps, functions, channels, interfaces and pointers are nilable. > Option types, on the other hand, are not a source of complexity, because they just use a more general language feature (ie variants). Go does not have variant types, so if adding option types requires them, you get an increase in the size of the language. In Go's case, variant types would have a weird interaction with interfaces, which arguably increases complexity.
- waps 12y ago> That's simply not true in Go. `nil` is not an inhabitant of all types. Integers, floats, strings, arrays and structs cannot be `nil`. Slices, maps, functions, channels, interfaces and pointers are nilable. True. But beside the point. Yes some things can be nil and some things can't. Problem is that there is nothing stopping me from doing this : func (x *X) { x.boem() } and that can crash. With ADTs ("option types (assuming a Haskell-like Maybe type) the compiler would complain : "x can be of type Nothing, so you can't just call a method on it". The point is that Option types mean that you can still have optional values, but you can never have nil pointers. > Go does not have variant types, so if adding option types requires them, you get an increase in the size of the language. In Go's case, variant types would have a weird interaction with interfaces, which arguably increases complexity. They would have exactly the same interaction with interfaces as they would have with anything else. ADTs are not of any definite type, so you have to case select them in most cases, and you can forego nil checking for everything else. You would have the "grouping" behavior anyway, since in Go you don't declare that you satisfy interfaces. So if all possibilities for an ADT implement the same interface, then the ADT should magically implement it too. I'm sure it'd be a change in the compiler, but it wouldn't be a change in the language.
- NateDad 12y agoI don't understand your problem with errors versus maps.... they're actually the exact same code pattern: val, ok := m["foo"] if !ok { // handle not found } // good path and val, err := m("foo") if err != nil { // handle err } // good path