- I too dislike the lack of a ternary operator. Python has this problem too (you can create boolean expressions to sorta mimic it but it doesn't tend to be considered "Pythonic"). And brevity is my reason too. I'm sure it's easier to parse without it but it can't be that hard.
- On the "damnable use requirement", I see his point. If anything, it means that Go will be better used with IDEs than text editors that'll do this for you automatically;
- On the "thread safe set", yeah he's Doing It Wrong [tm] (which I think he knows). You use channels to share state in Go rather than creating shared state directly.
- Unbuffered channels seem to be idomatic. Race conditions and deadlocks seem to often be the result of using buffered channels;
- On his channel reads issue ("violating time and space") I disagree: it's good to have blocking and non-blocking channel reads.
I basically agree with his conclusions, particularly in Go feeling like a "modern C", something I desperately hope succeeds.
will block, always. Iff the channel is closed, ok will be false, allowing you to distinguish between a zero value sent on the channel, and the zero value you get back if you try to receive on a closed channel. Receiving from a nil channel always blocks.
Select statements allow you to check if a channel has had a value sent on it.
Too much can be made of the idea of sharing state via channels. If it's simpler to express a piece of code using a mutex, use a mutex. A thread-safe set is one such situation. Don't feel bad about doing so; there is nothing non-idiomatic about using locks in Go.
When using channels, the choice between buffered and unbuffered depends on the situation. There are cases where a buffered channel is required to avoid deadlocks. For example, consider the case where you start N goroutines and use a channel to collect the results. If you return before collecting all results, the remaining goroutines will block forever trying to write to an unbuffered channel. Using a buffered channel with size N avoids this possibility.
Agreed, Rob Pike, in his search engine example at Google's I/O 2012 IIRC, made the explicit point that mutexes are there and that channels are for joining large concurrent parts of the program together; guarding a data structure seems too small even though it's a common (toy?) example. http://www.youtube.com/watch?feature=player_embedded&v=f...
Yes, I'm aware of that. But my philosophy is to always try to conserve keystrokes wherever possible. Premature optimization is the root of all evil and all that. :) For the same reason I prefer to use dict.items() over dict.iteritems() and range() over xrange() and so on.
This is a simple enough case that I'm not sure it matters, but I think the sum version is simpler to read. Generator expressions are quite powerful if you want to write Python which is more functional-flavored.
4) Should we strive to eliminate non-Boolean operations on bools
in the future, through suitable warnings, so that for example
True+1 would eventually (in Python 3000) be illegal?
=> No.
There's a small but vocal minority that would prefer to see
"textbook" bools that don't support arithmetic operations at
all, but most reviewers agree with me that bools should always
allow arithmetic operations.
6) Should bool inherit from int?
=> Yes.
In an ideal world, bool might be better implemented as a
separate integer type that knows how to perform mixed-mode
arithmetic. However, inheriting bool from int eases the
implementation enormously (in part since all C code that calls
PyInt_Check() will continue to work -- this returns true for
subclasses of int). Also, I believe this is right in terms of
substitutability: code that requires an int can be fed a bool
and it will behave the same as 0 or 1. Code that requires a
bool may not work when it is given an int; for example, 3 & 4
is 0, but both 3 and 4 are true when considered as truth
values.
Compatibility
Because of backwards compatibility, the bool type lacks many
properties that some would like to see. For example, arithmetic
operations with one or two bool arguments is allowed, treating
False as 0 and True as 1. Also, a bool may be used as a sequence
index.
I don't see this as a problem, and I don't want evolve the
language in this direction either. I don't believe that a
stricter interpretation of "Booleanness" makes the language any
clearer.
- They copied Pascal-style type declarations (good!) ... but then modified them to omit the colon (bad!).
That one little simple change really makes type declarations less readable, for no apparent benefit. Using a colon makes the type clearly stand out, whereas without one, it sort of gets lost amid the variables.
Pascal-style (Ada, etc, etc):
var foo, bar : int = 1, 2
Go:
var foo, bar int = 1, 2
The latter is uglier and harder to read, and doesn't save any appreciable space.
p.s. One saving grace: they could probably add colons back into the type-declaration syntax as an option, without affecting existing programs....
It's less of an issue with var declarations because of type inference. The bigger issue is with function signatures. Type signatures are mandatory there and you also have two levels of commas: func(a, b int, c, d bool). When it comes to syntax I generally prefer flat to nested (Python gets it right) but there is such a thing as too flat. A small saving grace is that exported identifiers in Go, including types, have to be capitalized, making them stand out in signatures.
To some, explicitly placing keywords like "public", "private", "protected" all throughout your code is ugly. Go gives an explicitly defined coding style; all Go will look roughly similar, and it will never be ambiguous if an identifier is exported or not.
> To some, explicitly placing keywords like "public", "private", "protected" all throughout your code is ugly.
I dunno; maybe it's "ugly," but it's also explicit, and easy to see. Assigning meaning solely to subtle presentational differences can make code hard to read, and increase the likelihood of mistakes (as well as confusing beginners).
[It's a similar problem to Python's significant whitespace.]
Unfortunately it's all this sort of "cute idea" that makes Go seem rather half-baked. It's like they designed the language in a brainstorming session in a bar, mixing ideas from a bunch of people without strong editorial control, and then just released the result without actually understanding the repercussions of many of their decisions. [I'm not saying they're all bad, it just feels like there was a lot more brainstorming than vetting...]
Unfortunately it's all this sort of "cute idea" that makes Go seem rather half-baked. It's like they designed the language in a brainstorming session in a bar, mixing ideas from a bunch of people without strong editorial control, and then just released the result without actually understanding the repercussions of many of their decisions.
This is as far from the truth as I can imagine.
Yes, "subtle presentational differences" can make code hard to read, but in this case it doesn't. When I reference another package I know that all exported variables begin with a capital letter. When I'm writing a package I know whether I'm calling a function that's "private" or "public" without having to look it up.
It's one of some of the Go Authors' favourite ideas in use in Go, and I wish people would stop armchairing about the effect some language feature has without trying it.
The likelihood of mistakes is not really increased, because they will be caught at compile time, or you'll end up with an exported function you didn't realise you wanted.
A confused beginner should be able to understand this concept within seconds. And it's prominently mentioned in the Go tutorials.
> I dunno; maybe it's "ugly," but it's also explicit, and easy to see. Assigning meaning solely to subtle presentational differences can make code hard to read, and increase the likelihood of mistakes (as well as confusing beginners).
I typically disagree when people say, "Well you haven't tried Go yet, so maybe you should," but this is one of the few cases where I agree. The exported/unexported syntax is weird to look at, but once you start using it, you quickly realize it to be amazing.
One of the key things about using a capital letter to indicate export/unexport is that you know if a function or a method is exported at the call site. That is, the export information isn't just in the function declaration, but in the name itself.
And trust me, it is not hard to read. It's not that hard to imagine that your eyes are quickly trained to see the export information.
> [It's a similar problem to Python's significant whitespace.]
No, that's not a problem for anyone who knows how to not mix tabs and spaces.
> and then just released the result without actually understanding the repercussions of many of their decisions
What's ironic is that you're criticizing a feature that they kept because of how it worked in the real world.
On the "violating time and space" issue, I don't think the problem was on there being both blocking and non-blocking channel reads. Instead the problem for him was that in his mind the channel read "happens first" and how the result from that read is used (i.e. what it is assigned to) shouldn't have any effect on the read anymore. I must say that I agree and would prefer to have a more clearly separate syntax for blocking and non-blocking reads.
Yeah, it's arbitrary that val, ok := <- ch does something different than val := <- ch, but any other convention for differentiating blocking from nonblocking would be just as arbitrary. I think it's something I would get used to after a while.
It's not so much arbitrary as an exception. "The RHS is evaluated and assigned to the LHS, except in this edge case". Using a different syntax would be just as arbitrary, but less of an exception e.g. val, ok := <- ch, val := <~ ch still follows the expected order of evaluation.
Thanks for the correction. Back when I first used go, they did different things. The change makes sense, I think-- we already have select for nonblocking I/O.
- On the "damnable use requirement", I see his point. If anything, it means that Go will be better used with IDEs than text editors that'll do this for you automatically;
The lack of partial/incremental/parallel compilation make this a lot less attractive. A lot of IDE technology (e.g., "intellisense," syntax errors, missing imports) are built off of rapid recompilation.
In fact, doing it any other way seems pretty stupid. If your compiler doesn't offer that information then the IDE has to re-implement those codepaths just to provide that information.
Fast is relative to purpose. Maybe could afford to be faster in the context of an IDE checking your code on the fly, for a large project. I wouldn't know, since I have not attempted to write it.
For information of readers, Python does have a ternary operator. I don't know or care whether it is considered Pythonic, since it is basic Python syntax and seems quite readable to me, e.g.
> it's good to have blocking and non-blocking channel reads.
That's not really his issue. His issue seems to be the overloading of the channel read to very different operations based on the number of return values.
I don't think he'd have minded if blocking and non-blocking channel reads were different operators, but it's the same operator returning either one or two values. It's harder to read.
- I too dislike the lack of a ternary operator. Python has this problem too (you can create boolean expressions to sorta mimic it but it doesn't tend to be considered "Pythonic"). And brevity is my reason too. I'm sure it's easier to parse without it but it can't be that hard.
- On the "damnable use requirement", I see his point. If anything, it means that Go will be better used with IDEs than text editors that'll do this for you automatically;
- On the "thread safe set", yeah he's Doing It Wrong [tm] (which I think he knows). You use channels to share state in Go rather than creating shared state directly.
- Unbuffered channels seem to be idomatic. Race conditions and deadlocks seem to often be the result of using buffered channels;
- On his channel reads issue ("violating time and space") I disagree: it's good to have blocking and non-blocking channel reads.
I basically agree with his conclusions, particularly in Go feeling like a "modern C", something I desperately hope succeeds.