Saturday, January 26, 2008

BlinkM out.

I'm tempted.

The problem with this being my "year of hardware" is that this means buying stuff rather than just downloading it free off the internet ... :-(

Thursday I went out and bought a soldering iron and multi-meter (the latter amazingly cheap) 15 Reais (about 4 quid). OTOH, I didn't buy breadboard which seemed (by comparison) pretty expensive to me. Does breadboard really cost 80-120 Reais (equiv. 20 - 30 UK pounds) for a small piece? Maybe it does and I'll need to get some.

At around 13 dollars (24 Reais / 7 quid) each, the blinkM looks pretty good value.

Actually, the price of coloured LEDs and microprocessors is now very, very cheap. We bought a 3-LED table decoration from a guy selling them in a bar the other week, for 8 Reais (2 quid, 4 dollars) The materials are easy to get hold of and cannibalize.

What makes blinkM interesting is that it's "programmable" - although they've had to make use of the Arduino to help with that. That makes everything more exciting. All the cheap LED novelty lights and nick-nacks being pumped out of china are just so much more useless junk. We don't want or need this stuff cluttering up our lives and environments. (The pollution cost is huge.)

But ... they are very, very close to becoming connected to the network. (Note the novelty USB hubs.) Once wired (or wirelessed) these items are no longer brain-dead successors to christmas tree fairy-lights. They are enchanted into information appliances, tendrils of the internet that can be used to convey useful information.

They aren't things that look beautiful and impressive for a minute / hour / week and then get put back in the box and forgotten about. Once, "smart", the life-span of a little ubicomp light or other device can extend indefinitely.

This is what Bruce Sterling is hoping for when he talks about spimes. A spime is an object who's identity and history are more important to us than its physical instantiation. Because we value these, the objects become individuals. We are less likely to dispose of them so easily, and when we do, we have a wealth of knowledge about how to disposee of them well. It's an ecologically optimistic vision of where we're heading.

My cheap table decoration, however beautiful, is likely to be landfill within 6 months (actually in my case, likely to be butchered) once we're bored and the batteries die. And then its on to buying the next shiny geegaw.

A BlinkM-like LED, by contrast, that found a useful niche in my personal information-flow ecology can sit around for years. I'm likely to maintain it (at least by buying it new batteries) and care about it.

Friday, January 25, 2008

Hmmm .... this does mean though that both sides of the conversation have to retain state.
Fascinating ... when you think about it.

Let's see if this really an example of the crowd being wiser than the pundits and theorists, or if the crowd are just followers, blowing around in a gale of random "projections" and catchy government programs.

Thursday, January 24, 2008

This looks like a must-read for me : Monads in Python

... and another Monad Tutorial I should read so I can understand it.
Depressing.

Hard to know how to stop this. The Amazon is way too big to police. There are always going to be people at the edge who are tempted to cut further into the forest. Do you devise a ring of "farm-free zones" all the way around? Prohibit exports of soya or crops from the surroundings? What about the millions of people who already live in the forest?
While everyone's side-tracked on the new US election and recession, let's not forget this, shall we?

The real issue here is that there's going to be a huge temptation for people who went along with Bush, or were enthusiastic supporters, to try to brush this aside saying "oh well, we weren't to know then" and "this is all academic because now Bush is on his way out."

It's not fucking academic! The same system, the same propaganda machinery, the same values, the same hidden agendas, the same media owners who hyped this up, the same public ignorance that let so many people find these stories plausible ... all the same conditions are all still operant.

Won't get fooled again? Sure you will.

Saturday, January 19, 2008

I promised to explain that little bit of python black-magic I posted a couple of days ago. It may take a couple of posts ...

But to start with, here's some code I just found, that I wrote about 18 months ago, to create a "pipeline" to do various transformations to lines of text. Basically I want to transform wiki markup into html, but I wanted a flexible mechanism which allowed me to add or remove various transformations at runtime so I can rejig the same transformation for slightly different targets. A good example of this occurs in the original SdiDesk, where the production of HTML to be viewed in SdiDesk is *almost* the same as the production of HTML for export to a static site, except for the link tags, who's href property for internal use needs to be "About:Blank" whereas the external site needs the actual URL of the page.

Here's my original code (with a couple of example transformations) ...





class PipeNode :
"""Base class for pipeline nodes.
They must implement
process() and getName()"""

def process(self, item) :
return item

def getName(self) : return "PipeNode"

class Pipeline :

def __init__(self) :
self.pipe = []
self.index = 0
self.item = None

def addNode(self, n) :
self.pipe.append(n)

def load(self, item) :
self.index = 0
self.item = item

def next(self) :
self.item = self.pipe[self.index].process(self.item)
self.index = self.index + 1

def getCurrentItem(self) :
return self.item

def getCurrentIndex(self) :
return self.index

def getCurrentNode(self) :
return self.pipe[self.index]

def eop(self) :
"""End of pipe"""
return self.index == len(self.pipe)

def run(self) :
while (not self.eop() ) :
self.next()

class BlankLines (PipeNode) :

def getName(self) : return "blankline-to-para"

def process(self, item) :
if item == '' :
return "<p/>"
else :
return item

class Bullet (PipeNode) :

def process(self, item) :
if item[0] == '*' :
return '<li>' + item[1:] + '</li>'
else :
return item

class LinewiseStringPipeline (Pipeline) :

"""
Based on the generic pipeline, this cuts a long string into lines
(separated by '\n') and processes them one by one
"""

def __init__(self, sep='\n') :
Pipeline.__init__(self)
self.sep = sep

def runAll(self, s) :
if len(self.pipe) == 0 :
return s
build = ''
lines = s.split(self.sep)
for x in lines :
self.load(x)
self.run()
build = build + self.getCurrentItem() + self.sep
build = build[0:-1]
return build

# test it
pl = LinewiseStringPipeline(r'\n')
pl.addNode( BlankLines() )
pl.addNode( Bullet() )

page = """here

is

* some
* data"""

print pl.runAll(page)




You'll see it's all good object oriented stuff. I define a base-class generic "PipeNode" which doesn't really do anything, but acts as a definition of the interface of a section of the pipe. Its important method is process, which takes an input and delivers some kind of output.

Then there's the pipe object itself. A straight-forward list of nodes which knows how to run ie. pull the items through each of the processing nodes. It keeps track of which section of the pipe the item is in using index.

Then there's the LinewiseStringPipeline which knows how to cut a large marked up page into single lines (wiki markup is applied on a line-by-line basis), run them separately through the pipe, and then concatenates them back together at the end.

Finally there are a couple of example PipeNode filters ... one which turns blank lines into blank paragraphs, and * into list-items.

I hope it's comprehensible. It's a little over-engineered with extra methods that are not strictly necessary for the operation. (Such as admin functions I guessed would be useful.)

And it is extremely verbose.

Now compare the same thing in my new, functional style which dispenses with objects and classes but seriously uses generators and closures.



def makeSection(f) :
def gen(source) :
for x in source :
yield f(x)
return gen


def makeFilter(test) :
def gen(source) :
for x in source :
if test(x) : yield x
return gen


def pipeline(generators) :
def p(source) :
old = source
for g in generators :
new = g(old)
old = new
return old
return p


def blankLines(item) :
if item == '' :
return "<p/>"
else :
return item


def bullet(item) :
if item[0] == '*' :
return '<li>' + item[1:].strip() + '</li>'
else :
return item


def linewisePipe(page, pipe) :
return '\n'.join(pipe(page.split('\n')))


# test it
pipe = pipeline([ makeSection(blankLines) ,
makeSection(bullet) ])
page = """here

is

* some
* data"""

print linewisePipe(page,pipe)




So, how does it work? Instead of handling each stage of the pipe with an object with only one significant method : process, it now handles each with a single function. Except that function is, in fact, a generator (ie. it's a function who's execution frame doesn't disappear when it yields it's return value, but continues in memory, and gets resumes where it left off).

The reason for this, is that these generators take a in iterator for their argument. And only pull and process the next item from the iterator before yielding it. The use of a generator is important because once it's up and running it's the generator itself which is remembering which item is currently being processed.

Of course, writing generators is a mildly more complicated than writing functions, so I've written some "make" functions to turn ordinary functions into them. makeSection(f) takes an ordinary x -> y function and returns the generator as a closure; note how the actual processing function is bound to f at the call of makeSection.

The result of calling makeSection, then, is a new generator which a) sucks something from an iterator (called "source"), b) calls f on it, c) yields it (ie. returns it, but hangs around with the next item from the iterator in "x", which it will yield next time it's called)

To simply use one of these sections on its own you could do something like this :




s = makeSection(lambda x: x * 2)
for x in s( [1,2,3,4,5] ) :
print x




Closure s takes an iterator as an argument (in this case, the list [1,2,3,4,5] ). It's result is also an iterator. (Which is what Generators look like from outside)

In fact the "for" loop keyword is implicitly calling next() on it every time to get a new value for x. When it does so, it pulls the next value out of s. Now, inside s, we have the body of "gen" as defined in makeSection. This is applying the function f (which doubles its argument x) on each value that comes through.

So sctions are always generator functions that are started with an iterator as input and at every step of the iteration return the next item from their source iterator transformed. The transformation itself depends on the argument given to the call to makeSection.

Filters are the same, except in this case, they only pass on items that meet the "test" criteria.

The next part of the program is to chain a number of these generators together into a pipeline. In fact, "pipeline" is another function which returns a closure. It takes a list of sections and filters and it's this list which is stored in the closure. (Be careful here, the reference to the list not the actual list content - so there's a danger of changing them after the execution of pipeline)

What comes out of pipeline is a new generator. One which takes an initial iterator as source, and on each next, sucks the next item from that source all the way through all the stages of the pipeline and spits it out.

So what are the advantages of this over my original object oriented version? It's certainly far "cleverer". To me, today, it looks more elegant. And it's much easier to start using. My specific transformation functions don't need to know anything about pipelines - they don't need to be defined as methods on new classes that are explicitly sub-classed from PipeNode. Instead each function can be written in ignorance of it's role, and then turned into a section or filter when needed. (Often just before assembling the pipe.)

Pipes are easy to assemble using the pipeline function, and I believe are very intuitive to use.

Of course, the object version could be tweaked to look more like the functional version to the outside user. The Pipeline object could be an iterator, making it possible to use it in a for loop. We could perhaps make it take a list of sections in it's constructor rather than force the programmer to write multiple explicit addNodes.

Nevertheless, I'm smitten with the new style. I'm replacing the old code with the new today.
Huggable Light
OrangeCone surveys magic rings

Friday, January 18, 2008

Arphid news

(NB : gonna start tracking stories about Arphids as part of ubicomp / spime interest.)
Lion Kimbro's PaperAnnotator

Related to pens?
Ian Bicking has a very thought-provoking post on Objects vs. Documents (Message passing vs. ReSTful resource manipulation) .

Thursday, January 17, 2008

Sweet ... Enso goes free-as-in-beer with hints as to free-as-in-speech too.

Maybe because they're taken over by / merging with Mozilla ...

That could mean some insanely great stuff if Mozilla becomes Humanized. Imagine every copy of firefox also being or bringing Enso. Imagine if web-sites could define new Enso services / plugins simply by embedding a descriptive chunk of XML in a page or on their site - (with the real work either being done in Javascript running locally or on their server). Imagine Enso as a standard command line to the web.


Like I say, sweet.
Don't know if I'm fully following this or understand exactly what a Ruby Fiber is ...

But it did inspire me to come up with this little bit of Python black-magic :



def makeSection(f) :
def gen(source) :
for x in source :
yield f(x)
return gen


def makeFilter(test) :
def gen(source) :
for x in source :
if test(x) : yield x
return gen


def pipeline(generators) :
def p(source) :
old = source
for g in generators :
new = g(old)
old = new
return old
return p

t2 = makeSection(lambda x : x * 2)
t3 = makeSection(lambda x : x * 3 )
f1 = makeFilter(lambda x : (x>20))
f2 = makeFilter(lambda x : (x<50))

pipe = pipeline( [ t2 , t3 , f1 , f2 ] )

for x in pipe(range(10)) : print x



See if you can work it out, I'll probably go over it tomorrow ...

Tuesday, January 15, 2008

Friday, January 11, 2008

Catching up with some posts. So here are a couple of new year's resolutions

1) I will get more into Brazilian music. (Instead of only seeking out the quirky un-Brazilian sounding stuff I hear here, I want to actually listen to what everyone says is great (bossa nova, classic samba, MPB etc.)

2) I'm going to get more into making things not just programs. Some electronics (not surprising given my ubicomp obsession) but also natural, low tech. materials. (Particularly wood and glass.)

3) I'm going to get out more ... 2006 was a good year for doing exercise and starting to worry about my physical condition. 2007 I did a lot of backsliding. Mainly because of new, time-greedy day-job. 2008 ... let's try to get back to 2006 levels and away from computer more.

4) er. ... but I will release GeekWeaver ... the real one which supports recursion and stuff. And a new OPTIMAES (will talk more about this soon) and sequels to SdiDesk and Gbloink! (See SmartDisorganized for more about my programming projects)

5) I will finally study and understand Monads (probably in Haskell)


... to be continued.
Great! So having spent their time in office and power (last 8 to 10 years) supporting the Israeli occupation, Blair and Bush, once they're gone (or going and effectively powerless), bother to actually go to Israel and have a look at the situation and end up calling for Israel to withdraw as part of a deal.

In other words they play the same corrupt logic of every politician who gets into this.

They accept the validity of Palestinian claim to statehood, yet somehow want to postpone it as a bargaining chip to use against those Palestinian political factions who are anti-Israel.

That is what keeps the whole thing in motion. If "the middle-eastern problem" is ever to be solved it's this logic that needs to be broken. There may or may not be a justified Palestinian claim to a separate state. But whether there is or not, it is not somehow conditional on Palestinians behaving themselves.

To treat the issue like that is identical to collectively punishing the whole Palestinian population for the behaviour of the actively anti-israeli sub-population.
Microsoft experimenting with multitouch using an inside-screen camera.

Rather like a close range version of Johnny Chung Lee's Wii-fingers

Thursday, January 10, 2008


Dear Blogger user,

This is a message from the Blogger team.

Your blog, at http://optimaes.blogspot.com/, has been identified as a potential spam blog.


Hey! Google! OPTIMAES is not a spam blog (even if it is a bit dormant). I've clicked the link to ask this to be reviewed ... but won't be able to post there until that's sorted.

Meanwhile, everyone else .... yes a 2008 resolution is to get back to OPTIMAES ... really.
Horrible news just after Christmas. My friend and one-time colleague Joe Holmburg died after fighting cancer for several months.

Joe's blog, recounting his experiences is here with a final note from his parents. And Scribe (who shared a house with Joe as an undergrad) writes movingly here.

Scribe also took this heartbreaking photo.

Don't need to say how sad I am about this. Joe was one of the sweetest guys you're likely to meet: smart, talented, good natured. Brilliant programmer, musician etc.

And only something like 28-29 years old.

Like a lot of people I can't quite believe this could have happened. It shouldn't have happened.

Like Scribe I've been trying to organize my thoughts about this and they've been getting muddled with the usual end-of-year retrospective thinking. I have a lot of half formed posts in my mind, and I've been holding off posting anything for a week, trying to find the right things to say. But you could wait forever for that. So I'll leave it as is and try to make more sense of them over the next few weeks and posts.

If you knew Joe, raise a glass (as many people are) to his memory and hope that there are more like him out there. And maybe listen to some Krauschanl while you're at it. If not, raise the glass anyway. And remember to live life to the full while it's still here.