Drawing into bitmaps and saving as a PNG in Swift on OS X

Pete Barber from C#, C++, Windows & other ramblings

Not an in depth post today. For a small iOS Swift/SpriteKit game I'm writing for fun I wanted a very basic grass sprite that could be scrolled; to create a parallax effect. This amounts to a 800x400 bitmap which contains sequential isosceles triangles of 40 pixels with random heights (of up to 400 pixels) and coloured using a lawn green colour.

Initially I was creating an SKShapeNode and creating the triangles above but when scrolling the redrawing of these hurt performance, especially when running on the iOS Simulator hence the desire to use a sprite.

I had a go at creating these with Photoshop. Whilst switching to a sprite improved performance the look of the triangles drawn by hand wasn't as good as the randomly generated ones. Therefore I thought I'd generate the sprite.

It wasn't really practical to do this on iOS as the file was needed in Xcode so I thought I'd try experimenting with a command line OS X (Cocoa) program in Swift. A GUI would possibly be nice to preview the results (and re-generate if needed) and to select the save-to file location but this solution sufficed.

I'd not done any non-iOS Swift development and never generated PNGs so various amounts of Googling and StackOverflow-ing was needed. Whilst the results of these searches were very helpful I didn't come across anything showing a complete program to create a bitmap, draw into it and then save so the finished program is presented below. It's also available as a gist.


1:  import Cocoa  
2:
3: private func saveAsPNGWithName(fileName: String, bitMap: NSBitmapImageRep) -> Bool
4: {
5: let props: [NSObject:AnyObject] = [:]
6: let imageData = bitMap.representationUsingType(NSBitmapImageFileType.NSPNGFileType, properties: props)
7:
8: return imageData!.writeToFile(fileName, atomically: false)
9: }
10:
11: private func drawGrassIntoBitmap(bitmap: NSBitmapImageRep)
12: {
13: var ctx = NSGraphicsContext(bitmapImageRep: bitmap)
14:
15: NSGraphicsContext.setCurrentContext(ctx)
16:
17: NSColor(red: 124 / 255, green: 252 / 255, blue: 0, alpha: 1.0).set()
18:
19: let path = NSBezierPath()
20:
21: path.moveToPoint(NSPoint(x: 0, y: 0))
22:
23: for i in stride(from: 0, through: SIZE.width, by: 40)
24: {
25: path.lineToPoint(NSPoint(x: CGFloat(i + 20), y: CGFloat(arc4random_uniform(400))))
26: path.lineToPoint(NSPoint(x: i + 40, y: 0))
27: }
28:
29: path.stroke()
30: path.fill()
31:
32: }
33:
34: let SIZE = CGSize(width: 800, height: 400)
35:
36: if Process.arguments.count != 2
37: {
38: println("usage: grass <file>")
39: exit(1)
40: }
41:
42: let grass = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(SIZE.width), pixelsHigh: Int(SIZE.height), bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSDeviceRGBColorSpace, bytesPerRow: 0, bitsPerPixel: 0)
43:
44: drawGrassIntoBitmap(grass!)
45: saveAsPNGWithName(Process.arguments[1], grass!)
46:

Naming is hard – or is it?

Phil Nash from level of indirection

Following Peter Hilton's excellent ACCU talk, at last week's conference in Bristol, "How to name things - the hardest problem in programming", a few of us were discussing some of the points raised - and some not raised.

He had discussed identifier length without any mention of Uncle Bob's guideline, whereby the length of a variable name should be proportional to it's scope (i.e. large or global scopes need longer, descriptive, names whereas in smaller, local, scopes shorter, more concise - even single letter - names are appropriate). This seemed all the more of an omission given that he later referenced the book, Clean Code.

It wasn't that Peter disgreed with Uncle Bob (who doesn't, half the time?) that surprised me but that he didn't even mention it in passing. I thought it was fairly well known. Actually I double checked and it is not discussed fully in the book, which only says, "The length of a name should correspond to the size of its scope". This is expanded considerably in Clean Coders (video) episode 2. Also, of course, this is not really "Uncle Bob's rule". Kevlin Henney recalls that he first heard of it in the 90s and it may well have been kicking around before that. Bob calls it "The Scope Rule".

Kevlin was one of those discussing this afterwards. After initially toying with The Scope Rule in the 90s he came to consider it not particularly useful. This, too, surprised me as I had found it worked quite well for me. Or so I thought. Further discussion with Kevlin led to the conclusion that I had read more of my own interpretation into The Scope Rule than I had realised! So I started musing over exactly what my interpretation was.

A transparent reference

As it happened a concept key to clarifying matters came from another great talk at the same conference just the day before. Didier Verna's "Referential transparency is overrated". In this talk Didier discussed various ways that useful idioms in Lisp required violating referential transparency. At one point he explained how "hidden" variables may be introduced by one macro that were then used by another. This worked because the thing being referred to was named very generically - so both macros agreed on the name. He drew on the term "Anaphora" from linguistics, which is where one part of an expression - usually a pronoun - stands in for a more specialised part - such as a person's name - introduced earlier in the context. For example, just now I used the word, "he" to refer to Didier Verna. It was clear who I was talking about because his was the most recently specified name in the current context. In fact I used this anaphoric term a couple of times - and many, many, times in this article. If I had had to fully qualify "Didier Verna" every time writing would quickly become very cumbersome. Anaphora is used very frequently in natural language - usually to good effect.

Scope Creep

I believe this is key to understanding why and when shorter identifiers can be used too. When I had been talking with Kevlin it had become apparent that we had different interpretations of the word, "scope". I realised that I had subconsciously expanded the specific technical meaning to include a more general idea of "context" - including the anaphoric context.

To make this clear I might write some (C++) code like this:

	std::string s = getNextString();
	if( !s.empty() )
		std::cout << "received string: " << s << std::endl;

Many corporate, or personal, coding standards would balk at such practice! Single character identifiers? Way to obfuscate the code!

But how has it obfuscated anything? Look at it as an anaphoric entity. In this case the variable name 's' is anaphoric. We know it is "the next string" because we saw it being introduced by the function call, "getNextString()". We then use it twice on the next couple of lines. There are no other strings being introduced in the same vicinity to confuse it with, and the context in which it is used is kept small. There is no ambiguity and the full identity is revealed in the immediate vicinity.

Sustainability

But what if we add more code, or move parts of this elsewhere? Certainly code evolves over time in ways that can make things less clear if we don't change them. That's true regardless. Naming of the entities at play should always form part of your consideration when refactoring or otherwise modifying existing code. Does it make this code less "sustainable" (to reference another property that Kevlin likes to talk about)? I don't think so. In the worst case, if you don't immediately notice that a short name has become unclear because it's usage has drifted out of anaphoric range, you'll notice the next time you look at it and, momentarily, think "why is there an free variable called 's' here? What on earth is that. You'll take a moment to find it's original declaration, work out what it is, then decide to encode that in the name by renaming the variable at that point. Variable renaming is one of the safest and most ubiquituous refactorings around so I have no qualms about deferring such identity expansion to such a time as it is needed.

Why?

But what about the other side of the argument? Is there any advantage to using a short, even single character, variable name in the first place?

This is often cast as a matter of optimising for typing speed - in world where we typically read these names many many times more than we write them.

While introducing, even small, speed bumps to writing code might discourage spending more time than necessary writing code (which in turn may discourage certain refactorings) it's not really about typing performance at all - It's about readability! Consider again the linguistic definition of anaphora: substituting an, unambiguous, subsequent reference to an entity with a shorter form (e.g. a pronoun) that means the same thing. We do this all the time in natural speech and the written word. Why? Because it would sound unnatural and cumbersome to fully qualify every entity we talk about all the time!

The same applies in programming. Where it is perfectly clear from the immediate context what an identifier refers to then using greater verbosity actually increases the cognitive friction! The more unnecessary and redundant noise and ceremony we can strip away from our code the easier it will be to read, in a shorter period of time. That fact that anaphora is so common in natural language should give us a clue as to our ability to code with it's use in a natural and efficient way.

Now I've only mentally organised my thoughts around this as a result of ruminating on those two talks - and some of the offshoot discussions - but I realise this is essentially how I had interpreted The Scope Rule. Now I've worked it through when I go back and compare it with what Mr Martin actually said his version sounds like a poor proxy for the anaphoric interpretation.

So naming - good naming - is still hard. We've only just discussed one narrow aspect here. But perhaps this has made some of it that little bit easier.

ACCU Conference 2015

Products, the Universe and Everything from Products, the Universe and Everything

We spent last week at the ACCU Conference in Bristol having our brains filled with gloopy tech goodness. It was (as ever) a real blast.

chandler_carruth presenting 'C++: Easier, Faster, Safer' at ACCU 2015

We had our demo rig with us as usual, and a steady stream of folks came along to chat to us and acquire caffeine from the expresso machine on the table next door. We came back absolutely exhausted, so no detailed photoblog this time I'm afraid!

However, for me the highlight was Chandler Carruth's closing keynote C++: Easier, Faster, Safer, in which he talked about how Google were using Clang and LLVM to (among other things) perform large scale automated refactoring (cue lots of furious scribbling...)

The synopsis of the keynote says it far better than I could:

Over the past five years, the prospect of developing large software projects in C++ has changed dramatically. We have had not one but two new language standards. An amazing array of new features are available today that make the language more elegant, expressive, and easy to use. But that isn't the only change in the last five years. LLVM and Clang have helped kick start a new ecosystem of tools that make developing C++ easier, faster, and safer than ever before.

This talk will cover practical ways you can use the tools we have built in the LLVM and Clang projects. It will show you what problems they solve and why those problems matter. It will also help teach you the most effective ways we have found to use all of these tools in conjunction with modern C++ coding patterns and practices. In short, it will show you how to make *your* C++ development experience easier, faster, and safer.

Next year's conference is provisionally scheduled for 19th-23rd April. See you there!

ACCU Conference 2015

Products, the Universe and Everything from Products, the Universe and Everything

We spent last week at the ACCU Conference in Bristol having our brains filled with gloopy tech goodness. It was (as ever) a real blast.

chandler_carruth presenting 'C++: Easier, Faster, Safer' at ACCU 2015

We had our demo rig with us as usual, and a steady stream of folks came along to chat to us and acquire caffeine from the expresso machine on the table next door. We came back absolutely exhausted, so no detailed photoblog this time I'm afraid!

However, for me the highlight was Chandler Carruth's closing keynote C++: Easier, Faster, Safer, in which he talked about how Google were using Clang and LLVM to (among other things) perform large scale automated refactoring (cue lots of furious scribbling...)

The synopsis of the keynote says it far better than I could:

Over the past five years, the prospect of developing large software projects in C++ has changed dramatically. We have had not one but two new language standards. An amazing array of new features are available today that make the language more elegant, expressive, and easy to use. But that isn't the only change in the last five years. LLVM and Clang have helped kick start a new ecosystem of tools that make developing C++ easier, faster, and safer than ever before.

This talk will cover practical ways you can use the tools we have built in the LLVM and Clang projects. It will show you what problems they solve and why those problems matter. It will also help teach you the most effective ways we have found to use all of these tools in conjunction with modern C++ coding patterns and practices. In short, it will show you how to make *your* C++ development experience easier, faster, and safer.

Next year's conference is provisionally scheduled for 19th-23rd April. See you there!

ACCU Conference 2015

Products, the Universe and Everything from Products, the Universe and Everything

We spent last week at the ACCU Conference in Bristol having our brains filled with gloopy tech goodness. It was (as ever) a real blast.
chandler_carruth presenting 'C++: Easier, Faster, Safer' at ACCU 2015
We had our demo rig with us as usual, and a steady stream of folks came along to chat to us and acquire caffeine from the expresso machine on the table next door. We came back absolutely exhausted, so no detailed photoblog this time I'm afraid! However, for me the highlight was Chandler Carruth's closing keynote C++: Easier, Faster, Safer, in which he talked about how Google were using Clang and LLVM to (among other things) perform large scale automated refactoring (cue lots of furious scribbling...) The synopsis of the keynote says it far better than I could:
Over the past five years, the prospect of developing large software projects in C++ has changed dramatically. We have had not one but two new language standards. An amazing array of new features are available today that make the language more elegant, expressive, and easy to use. But that isn't the only change in the last five years. LLVM and Clang have helped kick start a new ecosystem of tools that make developing C++ easier, faster, and safer than ever before.

This talk will cover practical ways you can use the tools we have built in the LLVM and Clang projects. It will show you what problems they solve and why those problems matter. It will also help teach you the most effective ways we have found to use all of these tools in conjunction with modern C++ coding patterns and practices. In short, it will show you how to make *your* C++ development experience easier, faster, and safer.
Next year's conference is provisionally scheduled for 19th-23rd April. See you there!

Groovy: Baby Steps

Tim Pizey from Tim Pizey

Posting a form in groovy, baby steps. Derived from http://coderberry.me/blog/2012/05/07/stupid-simple-post-slash-get-with-groovy-httpbuilder/


@Grab(group='org.codehaus.groovy.modules.http-builder', module='http-builder', version='0.7' )
@Grab(group='org.codehaus.groovyfx', module='groovyfx', version='0.3.1')
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.ContentType
import groovyx.net.http.Method
import groovyx.net.http.RESTClient

public class Post {

public static void main(String[] args) {

def baseUrl = "http://www.paneris.org/pe2/org.paneris.user.controller.LoginUser"

def ret = null
def http = new HTTPBuilder(baseUrl)

http.request(Method.POST, ContentType.TEXT) {
//uri.path = path
uri.query = [db:"paneris",
loginid:"timp",
password:"password"]
headers.'User-Agent' = 'Mozilla/5.0 Ubuntu/8.10 Firefox/3.0.4'

response.success = { resp, reader ->
println "response status: ${resp.statusLine}"
println 'Headers: -----------'
resp.headers.each { h ->
println " ${h.name} : ${h.value}"
}

ret = reader.getText()

println '--------------------'
println ret
println '--------------------'
}
}
}
}

Speaking: ACCU 2015

Pete Goodliffe from Pete Goodliffe

I'm pleased to announce that I'll be delivering the opening keynote at the awesome ACCU 2015 developer conference in Bristol this April. The talk is called "Becoming a Better Programmer" (it's no coincidence that this is the same title as my new book and my magazine column).

I'm really looking forward to it. I think it'll be great fun and, hopefully, really useful.

The session synopsis is:
You've come this conference to improve your skills. You're here to learn: to learn new technologies, to learn new techniques, and to fuel your passion by meeting like-minded people. 
Becoming a better programmer means more than just learning new technologies. It means more than practising techniques and idioms. It's about more than passion and attitude. It's the combination of all these things. That's what this session will look at. 
Pete Goodliffe, author of the new book Becoming a Better Programmer, unpacks important mindsets and techniques that will help you improve as a programmer.
You'll discover specific tools that will help you review your current skillset, and you'll learn techniques to help you “become a better programmer”.

More info about my session is available here.

Videos: MPC Sneak Peeks

Pete Goodliffe from Pete Goodliffe

In my day job I am the software lead for Akai's MPC product line. This is a product I'm immensely proud of, and I'm very proud of the the work the software team puts into this iconic music controller.

Over the last few months we've been producing in-house videos for the new releases we've been baking here at Akai towers.

We recently released the latest in this series for the 1.8.2 update. Check it out on YouTube here:

MPCv1.8.2 sneak peek

If you fancy seeing the whole set, we have also produced videos for:

The more recent of these have been shot completely in-house; I purchased a Canon 70D and some lights, and rigged up a Heath Robinson autocue. It's all shot in our development offices. Usually unnecessarily late into the night.

We love creating these videos, and connecting directly with the passionate MPC users.

Delete Jenkins job workspaces left after renaming

Tim Pizey from Tim Pizey

When renaming a job or moving one from slave to slave Jenkins copies it and does not delete the original. This script can fix that.


#!/usr/bin/env python
import urllib, json, os, sys
from shutil import rmtree

url = 'http://jenkins/api/python?pretty=true'

data = eval(urllib.urlopen(url).read())
jobnames = []
for job in data['jobs']:
jobnames.append(job['name'])

def clean(path):
builds = os.listdir(path)
for build in builds:
if build not in jobnames:
build_path = os.path.join(path, build)
print "removing dir: %s " % build_path
rmtree(build_path)

clean(sys.argv[1])


Event Processing with Transducers

Rob Smallshire from Good With Computers

In the previous article in this series on transducers we looked at lazily evaluating transducers. This time we'll look not at pulling output through a transducer chain from downstream, but at pushing input items into the chain from upstream.

All of the uses of transducers we've demonstrated in Python so far are probably better handled by existing and well established Python programming techniques, such as generator expressions and generator functions. At this point in the series, we move definitely beyond that into new territory where transducers bring completely new capabilities to Python.

One the key selling points of transducers is that they abstract the essence of a transformation away from the details of the data series that is being transformed. We'll show this in Python by using transducers to transform a series of events modelled using Python coroutines.

Coroutines in Python

Coroutines in Python are little-used, and their workings are not widely known, so their implementation bears repeating here. If you're familiar with the notion of coroutines in general, and the specifics of how they're implemented in Python, you can skim over this section.

Coroutines are like generator functions insofar as they are resumable functions. In fact, coroutines in Python are generator functions which use yield as an expression rather than a statement. What this means in practice is that generator function objects sport a send() method which allows the client of the generator function to transmit information to a running generator and for the generator to receive this data as the value of the yield expression. As usual, an example will serve to make things clearer.

We'll start by defining a generator function which enters an infinite loop, waits at the yield expression for a value to be received, and then prints this value to the console.

>>> def event_receiver():
...     while True:
...         message = (yield)
...         print("Message:", message)
...
>>>

We create a generator object just the same as we would with any other generator:

>>> r = event_receiver()
>>> r

Now we'll try to send it a message, using the send() method of the generator object:

>>> r.send("message")
Traceback (most recent call last):
  File "", line 1, in
TypeError: can't send non-None value to a just-started generator
>>>

This actually fails, because the generator code has not yet been executed at all. We need to prime the pump, so to speak, by advancing execution to the first occurrence of yield. We can do this by passing the generator to the next() built-in:

>>> next(r)

We'll fix this pump-priming annoyance of generator based coroutines shortly.

Now we can send messages:

>>> r.send("message")
Message: message
>>> r.send("another message")
Message: another message

When we're done, we terminate the coroutine by calling the close() method. (This actually raises a GeneratorExit exception at the site of the yield expression, which allows control flow to exit the otherwise infinite loop; this special exception is intercepted by the Python runtime system, so it isn't seen by us at the console).

>>> r.close()
>>>

Any further attempts to send() messages into the generator function cause StopIteration to be raised. This, of course, is the normal means of indicating that a generator is exhausted:

>>> r.send("message")
Traceback (most recent call last):
  File "", line 1, in
StopIteration
>>>

Priming generator-based coroutines

Now to address the awkwardness of having to prime coroutine generator functions by initially passing them to next(). We can improve this with a function decorator which creates the generator object and calls next on our behalf. We'll call the decorator @coroutine:

def coroutine(func):
    def start(*args, **kwargs):
        g = func(*args, **kwargs)
        next(g)
        return g
    return start

We'll use our new decorator to assist in defining a slightly more sophisticated coroutine for printing, called rprint():

import sys

@coroutine
def rprint(sep='\n', end=''):
    """A coroutine sink which prints received items to stdout

    Args:
      sep: Optional separator to be printed between received items.
      end: Optional terminator to be printed after the last item.
    """
    try:
        first_item = (yield)
        sys.stdout.write(str(first_item))
        sys.stdout.flush()
        while True:
            item = (yield)
            sys.stdout.write(sep)
            sys.stdout.write(str((item)))
            sys.stdout.flush()
    except GeneratorExit:
        sys.stdout.write(end)
        sys.stdout.flush()

In this implementation, we intercept GeneratorExit explicitly to give us the opportunity to print a terminator. We also regularly flush the stream so we get immediate feedback for our following experiments.

Event sources

The opposite of a sink is a source. Until now, we've been sourcing 'events' ourself by sending them from the REPL, but to make this a little more interesting, we'll cook up a function – just a plain old function, not a generator – which takes values from an iterable series and intermittently sends them, after a delay, to anything with a send() method such as our coroutine generators. For fun, the random delay has a so-called Poisson distribution which mimics a radioactive source; imagine a device with a geiger counter which sends the next item from an iterable series each time an atom decays:

def poisson_source(rate, iterable, target):
    """Send events at random times with uniform probability.

    Args:
      rate: The average number of events to send per second.

      iterable: A series of items which will be sent to the target
          one by one.

      target: The target coroutine or sink.

    Returns:
        The completed value, or None if iterable was exhausted
        and the target was closed.
    """
    for item in iterable:
        duration = random.expovariate(rate)
        sleep(duration)
        try:
            target.send(item)
        except StopIteration as e:
            return e.value
    target.close()
    return None

When either the iterable series is exhausted or the target signals it has terminated (by raising StopIteration) we call close() on the target. Note that by supplying an infinite iterable series we could make the source send events forever.

Let's hook our source and sink together at the REPL:

>>> printer = rprint(sep=', ', end='\nDONE!\n')
>>> count_to_nine = range(10)
>>> poisson_source(rate=0.5, iterable=count_to_nine, target=printer)
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
DONE!
>>>

Combined event sources and event sinks

Of course, we can build functions which act as both sinks and sources, transforming the messages they receive in some way and forwarding the processed results onwards to another sink. Here's a combined source and sink function, which simply doubles the values it receives:

@coroutine
def doubler(target):
    while True:
        item = (yield)
        doubled_item = item * 2
        try:
            target.send(doubled_item)
        except StopIteration as e:
            return e.value

To use, doubler() we chain the components of the pipeline together

>>> printer = rprint(sep=', ', end='\nDONE!\n')
>>> count_to_nine = range(10)
>>> poisson_source(rate=0.5,
...                iterable=count_to_nine,
...                target=doubler(target=printer))
0, 2, 4, 6, 8, 10, 12, 14, 16, 18
DONE!

From doubler() it's but a short hop to a more general mapper() which accepts an arbitrary transforming function:

@coroutine
def mapper(transform, target):
    while True:
        item = (yield)
        transformed_item = transform(item)
        try:
            target.send(transformed_item)
        except StopIteration as e:
            return e.value

Used like so,

>>> printer = rprint(sep=', ', end='\nDONE!\n')
>>> count_to_nine = range(10)
>>> poisson_source(rate=0.5, iterable=count_to_nine, target=mapper(transform=square, target=printer))
0, 1, 4, 9, 16, 25, 36, 49, 64, 81
DONE!

From here, you can see how we could also implement equivalents of filter(), reduce() and so on to operate on the 'push' event stream modelled by Python coroutines.

The point here is that we can't just re-use any existing functions which process 'pull' data series – such as the functions in itertools – with 'push' data series. Each and every function needs to be reimplemented to accept values pushed from upstream, and send processed results downstream.

Transducing events

Transducers provide a way out of this quandary. We've demonstrated earlier in this series that 'reduce' is a fundamental operation, and by reimagining reduce() into a more general transduce() we were able to use the same transducers to operate on both eager and lazy data series. We can do the same with coroutine-based push events, by implementing a version of transduce() which allows us to use any transducer to process a stream of such events.

Our reactive_transduce() is a coroutine which accepts two arguments: a transducer and a target sink to which the transduced results will be sent:

@coroutine
def reactive_transduce(transducer, target=None):
    reducer = transducer(sending())
    accumulator = target if (target is not None) else reducer.initial()
    try:
        while True:
            item = (yield)
            accumulator = reducer.step(accumulator, item)
            if isinstance(accumulator, Reduced):
                accumulator = accumulator.value
                break
    except GeneratorExit:
        pass
    return reducer.complete(accumulator)

The reactive_transduce() function connects to the upstream end of a transducer chain, adapting from the coroutine protocol to the reducer interface. At the downstream end of the transducer chain, we need to adapt the other way, from the reducer interface to the coroutine protocol. To do this we use a reducer called Sending, which we hard-wire as the 'bottom' reducer on the first line of reactive_transduce(). The Sending reducer looks like this:

class Sending:
    def initial(self):
        return null_sink()

    def step(self, result, item):
        try:
            result.send(item)
        except StopIteration:
            return Reduced(result)
        else:
            return result

    def complete(result):
        result.close()
        return result

The step() method literally sends the next item to the result – which must therefore be a legitimate event sink. Should the sink indicate that it can't accept a further item, by raising StopIteration we return the result wrapped in the Reduced sentinel. The initial() method provides a legitimate sink – just a simple do-nothing sink defined as:

@coroutine
def null_sink():
    while True:
        _ = (yield)

Going back to reactive_transduce() the main loop continues to iterate, receiving new values via a yield expression, until such time as GeneratorExit is signalled by the client or the reducer signals termination by returning Reduced.

When the main loop is exited by whatever means, we give the reducer opportunity to complete(), and the Sending.complete() method ensures that close() is called on the target.

With these pieces in place, let's look at how to use reactive_transduce(). We'll reproduce our previous example where we squared the output from poisson_source(), but this time using the mapping() transducer to do the work:

>>> poisson_source(rate=0.5,
...                iterable=range(10),
...                target=transduce(transducer=mapping(square),
...                target=printer))
...
0, 1, 4, 9, 16, 25, 36, 49, 64, 81
DONE!

The key point here is that we can now take an arbitrary transducer and reuse it with eager collections, lazy iterables, and push-events! In fact, simply by devising an appropriate transduce function we can use re-use our transducers in an arbitrary data-series processing context.

This is the true power of transducers: Data processing components completely abstracted away from how the input data arrives, or to where the output results are sent.