<div dir="ltr">Hi Eric,<div><br></div><div class="gmail_extra"><div class="gmail_quote">On Tue, Feb 24, 2015 at 3:05 PM, Eric Floehr <span dir="ltr"><<a href="mailto:eric@intellovations.com" target="_blank">eric@intellovations.com</a>></span> wrote:<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span class=""><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div class="gmail_extra"><div class="gmail_quote"><div>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.</div></div></div></div></div></blockquote><div><br></div></span><div>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.<br></div></div></div></div></blockquote><div><br></div><div>That's a good point, well said.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span class=""><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div class="gmail_extra"><div class="gmail_quote"><div>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:</div><div><br></div><div> <a href="https://github.com/joeshaw/talks/blob/master/cohpy/fetch.go" target="_blank">https://github.com/joeshaw/talks/blob/master/cohpy/fetch.go</a><br> </div></div></div></div></div></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div class="gmail_extra"><div class="gmail_quote"><div>notice that the fetch() function does not involve concurrency at all.</div></div></div></div></div></blockquote><div><br></div></span><div>Right, but the reason that works is because Go provides non-blocking HTTP and File operations by default.</div></div></div></div></blockquote><div><br></div><div>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.)</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>So how is Go's:<br></div><div><pre>for i := range sites {
site := sites[i]
go func() {
resultCh <- fetch(site)
}()
}
for res := range resultCh {
fmt.Println(res)
}
</pre><br></div><div>that different from Python 3's (assuming fetch used non-blocking http and file i/o, as Go):<br><pre>def func(site):
result = yield from fetch(site)
print result<br><br>funcs = [func(u) for u in sites]<br>asyncio.get_event_loop().run_until_complete(funcs)
<br></pre></div><div>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.</div></div></div></div></blockquote><div><br></div><div>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.</div><div><br></div><div>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: <a href="https://github.com/joeshaw/talks/blob/master/codecamp/concurrency2.go">https://github.com/joeshaw/talks/blob/master/codecamp/concurrency2.go</a></div><div><br></div><div>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.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><span class=""><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div class="gmail_extra"><div class="gmail_quote"><div></div><div>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.</div></div></div></div></div></blockquote><div><br></div></span><div>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.<br><br></div><div>But I don't understand how making non-concurrent code in Go concurrent any less "infectious" than making non-concurrent code in Python concurrent?<br></div></div></div></div></blockquote><div><br></div><div>"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.</div><div><br></div><div>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`.</div><div><br></div><div>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.</div><div><br></div><div>As mentioned earlier, the implicit event loop and yielding to the runtime in Go make it unnecessary.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>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.<br></div></div></div></div></blockquote><div><br></div><div>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.</div><div><br></div><div>I won't drag out the thread any longer, but there was a great blog post about asyncio recently: <a href="http://www.onebigfluke.com/2015/02/asyncio-is-for-composition.html">http://www.onebigfluke.com/2015/02/asyncio-is-for-composition.html</a> and a follow-up: <a href="http://www.onebigfluke.com/2015/02/ghost-of-threading-past.html">http://www.onebigfluke.com/2015/02/ghost-of-threading-past.html</a></div><div><br></div><div>Joe</div></div></div></div>