(Disclaimer: I have not spent enough time thinking thoroughly about all that I’m about to say.)
I often read about Go’s simplicity, and how that is an advantage. My question is: then, where does the complexity go? If we take the extreme example of untyped lambda calculus, it’s hard to imagine a simpler language than one that has 3 constructs. However, building applications on top of that simple core will be difficult, because the programmer will need to use Church encodings for conditional expressions, integers, etc. The language is simple because it transfers the brunt of the complexity over to the programmer.
Modify that lambda calculus to add conditionals, literals for numbers, data structures, etc. and you end up with a more complex language (i.e. there are more ways that the constructs can be combined together), however the developer doesn’t need to use hard-to-grok Church encodings, and can spend more time concentrating on the complexity of the problem he is trying to solve. The language took over the complexity that was going to be in the application anyway.
When I hear people argue that Go’s simplicity (i.e. not having exceptions, inheritance, parametric polymorphism, etc.) is a great benefit, I wonder how much of this simplicity comes from simply punting to the programmer.
(i.e. not having exceptions, inheritance, parametric polymorphism, etc.)
Note that all of these things have caveats, and that may be where the wires are getting crossed.
Go does not have exceptions, but it does have stack unwinding support with panic and defer that is less convenient that exceptions but more convenient than setjmp. I’ve used this to handle errors when they are abundant (and if err != nil { ... } becomes tedious).
Go does not have inheritance, but it does have composition with embedding. Embedding works with both interfaces and structs. It is a simple feature that you can get a lot of mileage from.
Go does not have parametric polymorphism, but it does have several built parametric types: map[T]T, []T and chan T. Those come with several built in parametric functions. This is an interesting feature because for some, this is tantalizing: the language is blessed with power that the user does not have. For others, it is a nice balance. For example, with these data types, you can trivially represent simple sets, hash tables, stacks, queues, arrays and concurrent queues. (The conspicuous absence here are graph based data structures.)
This is all pretty consistent with the worse is better design philosophy.
Go does not have exceptions, but it does have stack unwinding support with panic and defer that is less convenient that exceptions but more convenient than setjmp. I’ve used this to handle errors when they are abundant (and if err != nil { … } becomes tedious).
I also use panic/defer sometimes, when error handling becomes too tedious and I want to “aggregate” it somewhere up in the stack. I think that “raising” an error in Go is as convenient as in an exception-based language: panic is very similar to throwing an exception. It is only the “error catching” that is less convenient: defer/recover is a little more “low-level” and verbose than a catch block, but the semantics are also easier to understand, in my experience.
(Disclaimer: I have not spent enough time thinking thoroughly about all that I’m about to say.)
I often read about Go’s simplicity, and how that is an advantage. My question is: then, where does the complexity go? If we take the extreme example of untyped lambda calculus, it’s hard to imagine a simpler language than one that has 3 constructs. However, building applications on top of that simple core will be difficult, because the programmer will need to use Church encodings for conditional expressions, integers, etc. The language is simple because it transfers the brunt of the complexity over to the programmer.
Modify that lambda calculus to add conditionals, literals for numbers, data structures, etc. and you end up with a more complex language (i.e. there are more ways that the constructs can be combined together), however the developer doesn’t need to use hard-to-grok Church encodings, and can spend more time concentrating on the complexity of the problem he is trying to solve. The language took over the complexity that was going to be in the application anyway.
When I hear people argue that Go’s simplicity (i.e. not having exceptions, inheritance, parametric polymorphism, etc.) is a great benefit, I wonder how much of this simplicity comes from simply punting to the programmer.
Note that all of these things have caveats, and that may be where the wires are getting crossed.
Go does not have exceptions, but it does have stack unwinding support with
panicanddeferthat is less convenient that exceptions but more convenient thansetjmp. I’ve used this to handle errors when they are abundant (andif err != nil { ... }becomes tedious).Go does not have inheritance, but it does have composition with embedding. Embedding works with both interfaces and structs. It is a simple feature that you can get a lot of mileage from.
Go does not have parametric polymorphism, but it does have several built parametric types:
map[T]T,[]Tandchan T. Those come with several built in parametric functions. This is an interesting feature because for some, this is tantalizing: the language is blessed with power that the user does not have. For others, it is a nice balance. For example, with these data types, you can trivially represent simple sets, hash tables, stacks, queues, arrays and concurrent queues. (The conspicuous absence here are graph based data structures.)This is all pretty consistent with the worse is better design philosophy.
I also echo /u/zeebo’s comments.
I also use panic/defer sometimes, when error handling becomes too tedious and I want to “aggregate” it somewhere up in the stack. I think that “raising” an error in Go is as convenient as in an exception-based language: panic is very similar to throwing an exception. It is only the “error catching” that is less convenient: defer/recover is a little more “low-level” and verbose than a catch block, but the semantics are also easier to understand, in my experience.
Your comment reminds me of Guy Steele’s talk, “Growing a Language”: the video and the text of the talk can easily be found.