[CentralOH] Additional Go Language Influences

Joe Shaw joe at joeshaw.org
Tue Feb 24 22:27:04 CET 2015


Hi Eric,

On Tue, Feb 24, 2015 at 3:05 PM, Eric Floehr <eric at intellovations.com>
wrote:
>
> I think to get the kind of concurrency Go enjoys would require a Python
>> 4-like re-breaking of the language syntax, which I think would be a tough
>> sell. Maybe it could be done incrementally, I'm not sure.
>>
>
> I don't agree with this at all. To get everything that Go has, the way it
> has it, maybe. But if you want to write Go, write Go. But to enjoy
> concurrency as Go enjoys it (but perhaps implemented differently), it's
> already there in Python.
>

That's a good point, well said.


> One of the things I like best about Go is that goroutines allow you to
>> write code in a very linear, sequential, non-concurrent fashion and add
>> asynchronicity later.  If you look at my first example from the talk, the
>> concurrent URL fetcher:
>>
>>      https://github.com/joeshaw/talks/blob/master/cohpy/fetch.go
>>
>>
> notice that the fetch() function does not involve concurrency at all.
>>
>
> Right, but the reason that works is because Go provides non-blocking HTTP
> and File operations by default.
>

The main difference is that the Go runtime handles this, rather than
pushing it onto the programmer.  The code you write in Go appears
synchronous and the runtime handles yielding to other goroutines, whereas
in Python you have to explicitly yield.  (I wonder if this is something the
Python runtime could handle. I have some vague notion that another Python
implementation like PyPy or stackless Python might in fact do this.)

So how is Go's:
>
> for i := range sites {
>     site := sites[i]
>     go func() {
>         resultCh <- fetch(site)
>     }()
> }
>
> for res := range resultCh {
>     fmt.Println(res)
> }
>
>
> that different from Python 3's (assuming fetch used non-blocking http and
> file i/o, as Go):
>
> def func(site):
>     result = yield from fetch(site)
>     print result
>
> funcs = [func(u) for u in sites]
> asyncio.get_event_loop().run_until_complete(funcs)
>
> Sure you don't have channels in this example, but we don't really need
> them. And Go's event loop is implicit. I don't claim to be an expert in
> Python 3's asyncio so there may be even better ways to do this, but I don't
> really see any huge Go advantage or difference here.
>

This part is basically the same, but inside the fetch() function the Go
looks like it is a linear, blocking function while the Python one still has
an explicit yield in it (res = yield from aiohttp.get(site)) which
transforms it from returning a simple value to returning a generator.

You're right about the channel... it's not really necessary in the Go
version either.  A better example might be this one from my code camp talk:
https://github.com/joeshaw/talks/blob/master/codecamp/concurrency2.go

There I'm using channels more to build a pipeline: three independent,
concurrently running "machines": one that reads lines from
/usr/share/dict/words, one that gathers definitions from the OS X directory
services with 10 concurrent workers, and one that prints out the
definitions.

While I think the asyncio stuff in Python is really great and a big
>> improvement over things like gevent (or even Twisted), like C#'s
>> async/await syntax it "infects" your code by making it necessary to yield
>> within (or in C#, mark async) any code that needs to run asynchronously.
>> It can become a fairly large burden to convert code from being synchronous
>> to asynchronous because it often affects large portions of your call stack.
>>
>
> Coroutines have been first-class Python since 2.5, so sure, it's not baked
> in from the beginning. So a Go programmer is more likely to use async and
> goroutines from the beginning.
>
> But I don't understand how making non-concurrent code in Go concurrent any
> less "infectious" than making non-concurrent code in Python concurrent?
>

"Infectious" was perhaps a bad word choice because of its negative
connotation.  But what I meant by that was that to turn blocking Python
code into asyncio-friendly code you must yield a generator and either (a)
find a drop-in replacement like aiohttp more or less is for requests or (b)
use threading to bridge the blocking library with asyncio.

With the generator you need to explicitly unwrap it with something like
`run_until_complete` (and so it looks a lot like a Promise to me).  C# 5 is
similar in that functions have to be explicitly tagged `async` and then
`await` when in Python you would `yield`.

Quickly you can get into a situation where functions are nested and
cascading yields.  This was a common thing on a project I worked on in
Javascript (on Mozilla's spidermonkey, which had co-routines) where we had
implemented promises. It's definitely an improvement on "callback hell"
common in Node.js code but I think is still trickier than the
straightforwardness of Go's flow.

As mentioned earlier, the implicit event loop and yielding to the runtime
in Go make it unnecessary.


> Anyway, I do enjoy what Go offers, and have been very interested in the
> language. It does offer some cleaner syntax and built-in stuff, as well as
> bringing less baggage :-) that make it appealing.
>

Yeah, and I am not hating on Python, by the way.  I love it and still use
it.  I remember the bad old days of pre-coroutine Twisted deferreds and
asyncio is a breath of fresh air.  The closer Python can get to Go's
strengths in concurrency are positive for everyone... especially if you
strongly prefer Python to Go otherwise. :)  Happy feelings all around.

I won't drag out the thread any longer, but there was a great blog post
about asyncio recently:
http://www.onebigfluke.com/2015/02/asyncio-is-for-composition.html and a
follow-up: http://www.onebigfluke.com/2015/02/ghost-of-threading-past.html

Joe
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/centraloh/attachments/20150224/0f078324/attachment.html>


More information about the CentralOH mailing list