Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

A couple of responses:

- 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.



Go doesn't have non-blocking receives anymore.

  value, ok := <- ch
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.

http://golang.org/ref/spec#Receive_operator

https://groups.google.com/d/msg/golang-nuts/Z63l4LDOlsI/54uT...

http://golang.org/ref/spec#Select_statements


For those who didn't dive into the spec, the non-blocking recieve syntax has changed to use the select statement:

  func f(ch chan int) {
  	select {
  	case v := <-ch:
  		fmt.Println("got", v)
  	default:
  		fmt.Println("did not block")
  	}
  }
http://play.golang.org/p/ql0qSUVXeX


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...


> Python has this problem too

Python has had conditional expressions since version 2.5. Dumb example:

    def count(xs, p):
        return sum(1 if p(x) else 0 for x in xs)


Side note: you can simply use sum(p(x)) for x in xs), assuming p is a predicate. It's a neat trick, though a bit slower than your ternary version.


Another way: len(filter(p, xs))


With the sum approach the data set never exists in memory, but is generated as needed; not a big deal for a small set, but it can add up.


And as in python3, filter returns a generator, not a list, the len call will fail altogether.


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.


I disagree, I think filter/length models the meaning better.


I guess technically True==1 and False==0 in python and it is considered "pythonic" but personally I think it's "ugly".


I wouldn't even say it's Pythonic. As a reader, I would prefer len+filter (or ifilter). That's the most semantically clear.


I also prefer length + filter but while I dislike the style I think it is still considered pythonic.

The pep for adding bools to python: http://www.python.org/dev/peps/pep-0285/

    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.


> exported identifiers in Go, including types, have to be capitalized

Ugh... (notes another Go uglypoint)


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.


I hate Pascal-style type declarations. Using AS3 has given me a great hatred of that colon.


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.


It certainly does, but they're both blocking.

http://news.ycombinator.com/item?id=4569456


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.


Go generally compiles so quickly, incremental compilation is unnecessary.


Actually, incremental compilation is the main reason Go compiles so quickly.

Super-fast incremental compilation is the reason they never bothered with parallel compilation.


Go does incremental compilation just fine.

Nobody has bothered to add parallel compilation because building Go code is already so ridiculously fast.


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.


actually, the Go tool compiles packages in parallel by default.


...under the assumption that something does not depend on too many cgo packages.


Source? That's not what the article says.


The 8g, 6g, etc compilers operate on a single .go file at a time. You're welcome to invoke them directly if you want.


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.

x = (2 if y > 3 else 4)


> 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.


We need a preprocessor Come to add stub code for unused cars and imports.


No you don't

import ( _ "fmt" )

Try it.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: