<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3200437250410140982</id><updated>2011-12-07T04:35:45.790-08:00</updated><category term='first class functions'/><category term='coroutine'/><category term='yield'/><category term='curried'/><category term='query colon'/><category term='top down'/><category term='recycling'/><category term='dawkins'/><category term='consciousness'/><category term='tutorial'/><category term='monad'/><category term='android google cyanogen'/><category term='rhinos'/><category term='environment'/><category term='monads'/><category term='recursive descent'/><category term='lambda'/><category term='salonga'/><category term='evolution'/><category term='intelligent design'/><category term='kyoto'/><category term='intelligence'/><category term='memes'/><category term='python'/><category term='haskell'/><category term='darwinism'/><category term='parser'/><category term='c++'/><category term='bind'/><category term='generator'/><title type='text'>Nuerd</title><subtitle type='html'>The right way, the wrong way, or ... the other way.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>10</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-3364053582857029010</id><published>2011-12-07T03:38:00.001-08:00</published><updated>2011-12-07T04:31:34.819-08:00</updated><title type='text'>OpenGL ES 2.0 Gotchas</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;Perhaps I can save somebody some time by sharing what I learned on my first OpenGL ES 2.0 program:&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;b&gt;1) The coordinate system is probably left handed!&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;This may seem hard to believe bearing the entire history of OpenGL in mind, but it's certainly the case on my HTC Android phone and I've seen postings from confused iPhone programmers which seem to indicate the same. The ES 2.0 spec is agnostic about handedness, which seems incredibly dumb and destructive until you imagine the scene of twenty people sitting around a table all refusing to go home until they get their own way. When your eyelids are drooping, it doesn't matter how stupid the compromise is.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;You can check the handedness by drawing a tetrahedral triangle strip with a peak towards +z, making e.g. blue proportional to z, and rendering it with no transformations at all. If you see blue it's right handed, if not, left. I think we can rely on +y being towards the top of the screen.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;If you're accustomed to right handed systems, and you normally use your right hand to figure out the axes and rotations by pointing three fingers in the air at right angles or making a fist with your thumb sticking out, you just do all the same things with your left hand instead. The matrices are written in the usual way.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;b&gt;2) Z is clipped at +/- 1 whatever you put for glDepthRangef&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;I can't think of a good reason why they do this, but you can get around it by squashing z after the rotation and perspective transforms. You could simply write gl_Position.z*=0.01 at the end of your vertex shader, or you could pre-multiply your all-in-one matrix by a unit matrix with the 3x3 element replaced with 0.01.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;b&gt;3) Cube maps have the top at +y, not +z as you might expect.&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;'Nuff said.&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;If you see grey lines where the cubes join together, that means you have a discontinuity of texture coordinates over that boundary. For instance, consider the edge between the +y and +z faces. That edge is parameterised by x. You might have cocked up so that the +y face has x=0 at the left, but the +z face has x=0 at the right. If so, at e.g. x=0.25/0.75, OpenGL will try to interpolate the whole range of texture lookup results from 0.25 to 0.75 into the little triangle that straddles the boundary. The result is roughly grey. &amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;You can test this theory by fiddling with your mipmaps. You can make every mipmap below a certain level plain red. According to the theory, it has to mipmap very deeply to get that grey. If your grey lines turn red after you've painted the deep mipmaps red, then you've proved that discontinuities are indeed the problem. Naturally, this assumes you have mipmapping switched on at all.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;b style="font-family: Arial, Helvetica, sans-serif;"&gt;4) Matrices are the wrong way round&lt;/b&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;I suppose which way is the right way is arguable, but your matrix should be like&amp;nbsp;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;M[column][row].&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;b&gt;5) Render to texture&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;While you are rendering to the texture, the texture extends from (-1,-1) to (+1,+1), but when you're looking something up in it, it's (0,0) to (1,1).&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;b&gt;6) Performance&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;To speed things up, try to move everything from the fragment shader to the vertex shader. There's a quality cost here, but you might even like the resulting effects.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;b&gt;7) SGS 2&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;Just because it works on everything else, that doesn't mean it'll work on the SGS 2. If you meet somebody who's got one, be nice to them until they let you borrow it.&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: Arial, Helvetica, sans-serif;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-3364053582857029010?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/3364053582857029010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2011/12/opengl-es-20-gotchas.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/3364053582857029010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/3364053582857029010'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2011/12/opengl-es-20-gotchas.html' title='OpenGL ES 2.0 Gotchas'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-6748805573356360625</id><published>2010-01-23T01:46:00.000-08:00</published><updated>2010-01-23T05:37:17.819-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lambda'/><category scheme='http://www.blogger.com/atom/ns#' term='parser'/><category scheme='http://www.blogger.com/atom/ns#' term='monad'/><category scheme='http://www.blogger.com/atom/ns#' term='yield'/><category scheme='http://www.blogger.com/atom/ns#' term='coroutine'/><category scheme='http://www.blogger.com/atom/ns#' term='generator'/><category scheme='http://www.blogger.com/atom/ns#' term='top down'/><category scheme='http://www.blogger.com/atom/ns#' term='query colon'/><category scheme='http://www.blogger.com/atom/ns#' term='bind'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='first class functions'/><category scheme='http://www.blogger.com/atom/ns#' term='curried'/><category scheme='http://www.blogger.com/atom/ns#' term='recursive descent'/><title type='text'>Monadic parser in python</title><content type='html'>&lt;pre&gt;&lt;br /&gt;#!/usr/bin/python&lt;br /&gt;&lt;br /&gt;# general stuff&lt;br /&gt;id = lambda v : v&lt;br /&gt;nott = lambda b : not b&lt;br /&gt;fst = lambda (v,u) : v&lt;br /&gt;snd = lambda (v,u) : u&lt;br /&gt;eq = lambda w : lambda v : w==v&lt;br /&gt;compose = lambda f,g : lambda x : f(g(x))&lt;br /&gt;exists = compose(nott, eq(None))&lt;br /&gt;&lt;br /&gt;# no query-colon in python even though it has lambdas    &lt;br /&gt;# simpler forms than this dont work because python isn't lazy enough&lt;br /&gt;#   so we get some superflous "lambda _ :"s later on&lt;br /&gt;# the foos are only numbered to help with debugging - they could all have the same name&lt;br /&gt;# (in python, you need a def instead of a normal lambda if you want ifs or assignments or &lt;br /&gt;#  other actions in there, or if you want to delay evaluation of things refered to in the function)&lt;br /&gt;&lt;br /&gt;def query(test,yes,no):&lt;br /&gt;    def foo4(v):&lt;br /&gt;        if test(v):&lt;br /&gt;            return yes(v)&lt;br /&gt;        else:&lt;br /&gt;            return no(v)&lt;br /&gt;    return foo4&lt;br /&gt;&lt;br /&gt;# underscores in variable names are a convention meaning we don't care&lt;br /&gt;def throw(_):&lt;br /&gt;    raise Exception&lt;br /&gt;&lt;br /&gt;# a string-wrapping generator that repeatedly takes a filter function, &lt;br /&gt;#  applies it to the head of the stream, and if it passes, returns &lt;br /&gt;#  the character and moves along to the next one in the stream&lt;br /&gt;#  or otherwise returns None and doesn't move along&lt;br /&gt;# getting these generators in sync is a trial-and-error job&lt;br /&gt;def nudger(str):&lt;br /&gt;    def foo1(i):&lt;br /&gt;        progress=0&lt;br /&gt;        test = yield # see four lines below&lt;br /&gt;        c = i.next() # consume first character from raw stream&lt;br /&gt;        while True:&lt;br /&gt;            if test == "Where": # for error reporting&lt;br /&gt;                test = yield progress # waits for a send, sets &lt;br /&gt;                # test to send's parmeter, runs to the next yield, &lt;br /&gt;                # and then returns from send with progress as its return value&lt;br /&gt;            elif test != None and c != None and test(c): # IF it matches, &lt;br /&gt;                test = yield c #yield it and pull another from raw stream                &lt;br /&gt;                try:&lt;br /&gt;                    c = i.next()&lt;br /&gt;                    progress += 1&lt;br /&gt;                except StopIteration:&lt;br /&gt;                    c = None&lt;br /&gt;            else: #ELSE yield None and don't pull&lt;br /&gt;                test = yield None&lt;br /&gt;    s = foo1(iter(str))&lt;br /&gt;    s.next() # always gotta do this before sending to it&lt;br /&gt;    return s&lt;br /&gt;&lt;br /&gt;# wrapped test string&lt;br /&gt;s = nudger("X3$5z2[3]1,2_1984foo1066bar1000*(25+3)-100*(5-10*(11/4))+1")&lt;br /&gt;&lt;br /&gt;# just hide those ugly sends   &lt;br /&gt;filter = lambda test : lambda s : s.send(test) &lt;br /&gt;# some basic filters&lt;br /&gt;this = that = lambda want : filter(lambda c : c == want) # get specific character&lt;br /&gt;member = lambda set : filter(lambda c : c in set) # get anything in set&lt;br /&gt;between = lambda min,max : filter(lambda c : &lt;br /&gt;   ord(c)&gt;=ord(min) and ord(c)&lt;=ord(max)) # get within range&lt;br /&gt;either = lambda test1,test2 : lambda s : test1(s) or test2(s) #or&lt;br /&gt;&lt;br /&gt;# A parser is a function that chomps some characters out of a passed stream &lt;br /&gt;#  and returns a value corresponding to what it chomped&lt;br /&gt;# If it doesn't find the first character of what it wanted must leave &lt;br /&gt;#  the stream unchanged and return None&lt;br /&gt;# If it finds just some of what it expected it must raise an exception &lt;br /&gt;#  (LL(1) grammars only) &lt;br /&gt;# It may ignore the stream and just return a value it already knows&lt;br /&gt;# These are parsers:&lt;br /&gt;&lt;br /&gt;digit = between("0","9")    &lt;br /&gt;letter = either(between("A","Z"),between("a","z"))&lt;br /&gt;white = member([" ",chr(10),chr(13),chr(9)])&lt;br /&gt;&lt;br /&gt;print this("$")(s) #should fail&lt;br /&gt;print letter(s)&lt;br /&gt;print digit(s)&lt;br /&gt;print this("$")(s)&lt;br /&gt;&lt;br /&gt;# Monadic bind : easy version&lt;br /&gt;# If this fries your brain, start by reading "compressed" or "inbrackets" below &lt;br /&gt;# Takes a parser "first", a function "next" and returns a parser "big"&lt;br /&gt;# The next function is supposed to return a parser when called with a value&lt;br /&gt;# The big parser (when applied to a stream) runs the first parser, &lt;br /&gt;#  shoves its return value into the next function,&lt;br /&gt;#  runs the resulting parser and returns its result&lt;br /&gt;# If the first parser fails, the next function is not called but None is returned&lt;br /&gt;# If the second parser fails, we throw an exception&lt;br /&gt;&lt;br /&gt;# Monadic bind: fancy version:&lt;br /&gt;# Adds customisation of what's returned if the first parser fails, &lt;br /&gt;#  and pre and post functions to be applied:&lt;br /&gt;#  to the first parser's return value before passing it to the next function and&lt;br /&gt;#  to both parsers' return values to decide what to return from the big parser, respectively&lt;br /&gt;# The easy post function returns its second parameter unchanged but throws if it's None. &lt;br /&gt;# We can remove that throw for productions of indeterminate length&lt;br /&gt;# Pre is a bit of a hack but otherwise we'd need a state monad, and this ain't a pure language anyway&lt;br /&gt;# Post is legit&lt;br /&gt;&lt;br /&gt;def bindfancy(fp,dv,pre,post,nf):&lt;br /&gt;    def foo3(s):&lt;br /&gt;        v1 = fp(s)               #call the first parser&lt;br /&gt;        if v1 != None:&lt;br /&gt;            p2 = nf(pre(v1))     #pre-process the answer and pass it to the next function &lt;br /&gt;            v2 = p2(s)           #run the parser resulting from the above&lt;br /&gt;            return post((v1,v2)) #figure out the overall return value&lt;br /&gt;        else:&lt;br /&gt;            return dv            # if first parser fails return the default&lt;br /&gt;    return foo3&lt;br /&gt;&lt;br /&gt;# Good for most cases:&lt;br /&gt;bindeasy = lambda fp,nf : bindfancy(fp,None,id,compose(query(exists,id,throw),snd),nf)&lt;br /&gt;&lt;br /&gt;# If the next parser returns None, don't throw but return result of first:&lt;br /&gt;bindmaybe = lambda fp,nf : bindfancy(fp,None,id,query(compose(exists,snd),snd,fst),nf)    &lt;br /&gt;&lt;br /&gt;# Get a digit n then a character c and return c repeated n times&lt;br /&gt;compressed = bindeasy(digit,  lambda n :       # Get a digit called n (it's a string, not an int)&lt;br /&gt;             bindeasy(letter, lambda c :       # get a letter called c&lt;br /&gt;             lambda s :       n_of(int(n),c))) # ignore the stream and return int(n) c's   &lt;br /&gt;&lt;br /&gt;n_of = (lambda n,c :                     # query returns a function and we will throw n at it&lt;br /&gt;        query(eq(0),                     # is query's result's parameter zero?&lt;br /&gt;              lambda _:'',               # then the answer is '' irrespective of query's result's parameter&lt;br /&gt;              lambda _: n_of(n-1,c) + c) # else recurse with n-1 and append c irrespective of query's result's parameter&lt;br /&gt;        (n))                             # apply n to the ?: we just made&lt;br /&gt;&lt;br /&gt;print compressed(s)&lt;br /&gt;try:&lt;br /&gt; print compressed(s)&lt;br /&gt;except Exception:                        # this tute isn't about exceptions&lt;br /&gt; print "Syntax error at character %d" % s.send("Where")&lt;br /&gt;&lt;br /&gt;inbrackets = (lambda open,close :               # build a thing that modifies ... &lt;br /&gt;              lambda p :                        # ... another parser&lt;br /&gt;              bindeasy(this(open),  lambda _a : # get a [ and forget it (_ convention)&lt;br /&gt;              bindeasy(p,           lambda v :  # get a p called v&lt;br /&gt;              bindeasy(this(close), lambda _b : # get a ] and forget it&lt;br /&gt;              lambda s :            v           # ignore stream and return v&lt;br /&gt;              ))))&lt;br /&gt;&lt;br /&gt;print inbrackets('[',']')(digit)(s)&lt;br /&gt;&lt;br /&gt;infix = (lambda p1,sep,p2,comb :          # get a separated pair but be flexible about&lt;br /&gt;         bindeasy(p1,        lambda v1 :  #  the separator and how to combine the two values&lt;br /&gt;         bindeasy(this(sep), lambda _ :&lt;br /&gt;         bindeasy(p2,        lambda v2 :&lt;br /&gt;         lambda s :          comb(v1,v2)&lt;br /&gt;         ))))&lt;br /&gt;&lt;br /&gt;coord = infix(digit,",",digit, lambda a,b : a+" by "+b)&lt;br /&gt;&lt;br /&gt;print coord(s)          &lt;br /&gt;print this("_")(s)&lt;br /&gt;&lt;br /&gt;# Recursive monad: &lt;br /&gt;# word_ is the "next" function &lt;br /&gt;# its parameter is the word so far&lt;br /&gt;# if parsing fails it returns the word so far&lt;br /&gt;# if it succeeds it adds the letter it found to the word and passes it&lt;br /&gt;# to the next function, which is _word&lt;br /&gt;&lt;br /&gt;word_ = (lambda sofar :              # this is not a parser YET ,,, &lt;br /&gt;         bindfancy(letter,           # the first parser &lt;br /&gt;                   sofar,            # what to return if first parser fails&lt;br /&gt;                   lambda l:sofar+l, # the next round's sofar&lt;br /&gt;                   snd,              # same as bindeasy&lt;br /&gt;                   word_             # the "next" function&lt;br /&gt;                  ))&lt;br /&gt;&lt;br /&gt;word = word_("")                     # ,,, NOW it's a parser&lt;br /&gt;&lt;br /&gt;# generalising that:&lt;br /&gt;# f should take sofar, then the value found by the parser &lt;br /&gt;# and return the value you want passed as sofar to the next round&lt;br /&gt;&lt;br /&gt;def zeroormore(p,i,f):              # after providing these three you get an almost-parser&lt;br /&gt;    def foo5(sofar):                #  like word_. provide sofar and you have a parser&lt;br /&gt;        return bindfancy(p,          &lt;br /&gt;                         sofar,&lt;br /&gt;                         f(sofar),  # give f sofar and it returns a function of parse result &lt;br /&gt;                         snd,       #  onto the next round's sofar&lt;br /&gt;                         foo5)      # recurse&lt;br /&gt;    return foo5(i)                  # this is like word = word_("")&lt;br /&gt;    &lt;br /&gt;number = zeroormore(digit,&lt;br /&gt;                    0,&lt;br /&gt;                    lambda sofar: lambda v: 10*sofar+int(v) ) &lt;br /&gt;# but that returns 0, not None, if it finds nothing&lt;br /&gt;&lt;br /&gt;print number(s)       &lt;br /&gt;print word(s)&lt;br /&gt;  &lt;br /&gt;# oneormore: do one normal parse of p1 then go into zeroormore with p2&lt;br /&gt;# bindfancy applies pre to the result of the first parse &lt;br /&gt;#  before passing it to zeroormore as sofar&lt;br /&gt;&lt;br /&gt;oneormore = (lambda p1,pre,p2,f : &lt;br /&gt;             bindfancy(p1, &lt;br /&gt;                       None, &lt;br /&gt;                       pre,                       # result of this on p1's result is v&lt;br /&gt;                       snd, &lt;br /&gt;                       lambda v : zeroormore(p2, &lt;br /&gt;                                             v,   # return this on fail &lt;br /&gt;                                             f))) # f combines v with results of p2s for new v&lt;br /&gt;&lt;br /&gt;number = oneormore(digit, int, digit, lambda sofar: lambda v: 10*sofar+int(v))&lt;br /&gt;word   = oneormore(letter, id, letter, lambda sofar: lambda v: sofar+v )&lt;br /&gt;&lt;br /&gt;print number(s) &lt;br /&gt;print word(s)&lt;br /&gt;&lt;br /&gt;# use the first parser in the list that works&lt;br /&gt;def anyof(plist): &lt;br /&gt;    def foo6(s):&lt;br /&gt;        for p in plist:&lt;br /&gt;            v = p(s)&lt;br /&gt;            if v != None:&lt;br /&gt;                return v&lt;br /&gt;    return foo6&lt;br /&gt;&lt;br /&gt;#this lot has to be in defs because it's circular:&lt;br /&gt;# otherwise this is just like factor = anyof([ number , inbrackets('(',')')(expression) ])&lt;br /&gt;def factor(s):&lt;br /&gt;    return anyof([ number,&lt;br /&gt;                   inbrackets('(',')')(expression) ])(s)                             &lt;br /&gt;&lt;br /&gt;def term(s):       &lt;br /&gt;    return oneormore(factor, &lt;br /&gt;                     id, &lt;br /&gt;                     anyof([ bindeasy(this("*"), lambda _ : &lt;br /&gt;                             bindeasy(factor, lambda n : &lt;br /&gt;                             lambda s : n ))&lt;br /&gt;                             ,&lt;br /&gt;                             bindeasy(this("/"), lambda _ : &lt;br /&gt;                             bindeasy(factor, lambda n : &lt;br /&gt;                             lambda s : 1.0/n)) ]), &lt;br /&gt;                     lambda sofar : lambda m : sofar * m)(s)&lt;br /&gt;&lt;br /&gt;def expression(s):       &lt;br /&gt;    return oneormore(term, &lt;br /&gt;                     id, &lt;br /&gt;                     anyof([ bindeasy(this("+"), lambda _ : &lt;br /&gt;                             bindeasy(term, lambda n : &lt;br /&gt;                             lambda s : n ))&lt;br /&gt;                             ,&lt;br /&gt;                             bindeasy(this("-"), lambda _ : &lt;br /&gt;                             bindeasy(term, lambda n : &lt;br /&gt;                             lambda s : -n )) ]), &lt;br /&gt;                     lambda sofar : lambda m : sofar + m)(s)&lt;br /&gt;&lt;br /&gt;print expression(s)&lt;br /&gt;&lt;br /&gt;# Exercise (i.e. the bit I couldn't be bothered to finish): &lt;br /&gt;# write a generator that reads s and strips out anything that&lt;br /&gt;#  fits the passed parser e.g. comments and/or whitespace and can be&lt;br /&gt;#  used in place of s throughout the above.&lt;br /&gt;# I.e.: fix strip and the comments so that:&lt;br /&gt;&lt;br /&gt;blockcomment = linecomment = white   # write something more interesting&lt;br /&gt;strip = lambda p : lambda s : s      # figure this out&lt;br /&gt;&lt;br /&gt;# and use just like this:&lt;br /&gt;print expression( strip(blockcomment)(&lt;br /&gt;                  strip(white)( &lt;br /&gt;                  strip(linecomment)(   # better do linecomments before removing newlines&lt;br /&gt;                  s)))) &lt;br /&gt;&lt;br /&gt;# Better still, make it a pre-processor that can replace the found bit with something&lt;br /&gt;#  dependant on it.&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-6748805573356360625?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/6748805573356360625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2010/01/monadic-parser-in-python.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/6748805573356360625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/6748805573356360625'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2010/01/monadic-parser-in-python.html' title='Monadic parser in python'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-2481777886789276973</id><published>2009-09-30T06:34:00.001-07:00</published><updated>2009-09-30T06:52:42.002-07:00</updated><title type='text'>A program to write a program</title><content type='html'>I'm in the middle of writing a program to write a program. I'm not sure what kind of program it's going to write. I know that it'll be in Intel machine code though (and I don't mean assembly language.)&lt;br /&gt;&lt;br /&gt;This is a bit like Corewars, only I'm not inventing any game. The game is to run on an Intel computer, but I didn't invent that. There's a limited amount of space and processor time on this computer, and there are lots of conceivable code snippets that can copy themselves, with or without variations. Even if they copy themselves perfectly, other snippets are likely to overwrite them. If the system doesn't get into an endless loop, I think I can expect evolution to break out at some point. I can make sure it doesn't get into an endless loop by overwriting snippets myself. I can just flip bits in the memory at random.&lt;br /&gt;&lt;br /&gt;My code boots the chip into protected mode and sets up handlers for all the faults Intel know as well as a periodic interrupt. It then creates a ring 3 segment ("tank") aliased as both code and writable data, fills it with random numbers and jumps into it. When faults or timers occur, a few bits near the last point of execution get flipped, and control jumps back in somewhere else.&lt;br /&gt;&lt;br /&gt;I'm pretty sure this must start evolving eventually, but I'm not sure how long I'll have to wait. To find out if anything is happening, I just need to zip the tank. A small zipfile would indicate evolution.&lt;br /&gt;&lt;br /&gt;The code runs in VMWare Player, is about three quarters done and you can get it from here: &lt;a href="http://github.com/adrianmay/digilife"&gt;http://github.com/adrianmay/digilife&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-2481777886789276973?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/2481777886789276973/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2009/09/program-to-write-program.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/2481777886789276973'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/2481777886789276973'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2009/09/program-to-write-program.html' title='A program to write a program'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-6077991372864123946</id><published>2009-09-29T08:14:00.000-07:00</published><updated>2009-10-04T21:36:12.239-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rhinos'/><category scheme='http://www.blogger.com/atom/ns#' term='salonga'/><category scheme='http://www.blogger.com/atom/ns#' term='kyoto'/><category scheme='http://www.blogger.com/atom/ns#' term='recycling'/><category scheme='http://www.blogger.com/atom/ns#' term='environment'/><title type='text'>Things the environmentalists don't tell you</title><content type='html'>Recycling paper actually reduces the number of trees in the world by filling the demand for wood pulp, which is mainly supplied by the kind of soft, fast growing trees you can see lined up in rows all over Scotland. It becomes more lucrative to put greenhouse gas farting sheep on the land instead.&lt;br /&gt;&lt;br /&gt;In the Salonga national park in Congo, the conservationists chase Pygmies out of the forest because they hunt bushmeat. Let that be a warning to any jaguars reading.&lt;br /&gt;&lt;br /&gt;If we just threw all the plastic bottles into the sea, in a million years time there would probably be a cute new species of octopus using them as a shell.&lt;br /&gt;&lt;br /&gt;Brazil is the number one producer of bio-diesel. It is also home to most of the world's rain forests. Do you still believe that bio-diesel is carbon-neutral?&lt;br /&gt;&lt;br /&gt;The World Wildlife Fund are supporting a program to cut the horns off Rhinos in Zimbabwe and Namibia. The idea is to make it commercially unattractive to poach them. Preliminary results indicate a 100% calf mortality rate due to predators the dehorned cows cannot fight off, while poaching continues for the sake of what scraps of horn remain.&lt;br /&gt;&lt;br /&gt;Most of the species we go dewy eyed about (whales, polar bears, tigers, etc) owe their existence to an extreme ecological catastrophe 65 million years ago. The ones we don't like (mosquitos, cockroaches, crocodiles, etc) were unaffected.&lt;br /&gt;&lt;br /&gt;The Kyoto Protocol does not prevent global warming. In fact it barely even slows it down. It just spends a phenomenal amount of money shutting the door after the horse has bolted. Adapting to the new climate will also cost a lot of money but without us having the opportunity to decide whether we want to spend it or not. Perhaps we should stop wasting time and money failing to postpone the inevitable, and start spending money preparing for it before it's too late.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-6077991372864123946?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/6077991372864123946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2009/09/things-environmentalists-dont-tell-you.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/6077991372864123946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/6077991372864123946'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2009/09/things-environmentalists-dont-tell-you.html' title='Things the environmentalists don&apos;t tell you'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-2157747582357806475</id><published>2009-09-29T07:33:00.000-07:00</published><updated>2009-09-29T08:00:17.044-07:00</updated><title type='text'>Yet another project health check</title><content type='html'>Pick a random line of code from your large, buggy project and ask yourself the following question about it.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Was the problem it solves created by the requirements, or by the code?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Repeat that 100 times and you've got your percentage health score. Most large projects spend about 95% of their code worrying about the code.&lt;br /&gt;&lt;br /&gt;For instance:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   hr = CoCreateInstance(CLSID_SomeDummyObj, NULL, CLSCTX_INPROC, &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is definitely solving a problem in the code. It's pure &lt;span style="font-style: italic;"&gt;Blindleistung&lt;/span&gt; resulting from the choice of technology, so that's bad.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   if ( bus-&gt;driver-&gt;teatime() &gt; now() ) bus-&gt;findnewdriver();&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is all about logic that the requirements engineer would recognise, so that's good.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family:courier new;"&gt;   getcommandqueue()-&gt;enqueuecommand(new SingCommand(song));&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is all codey code. The code to sing the song is a member of the SingCommand, so if you'd chosen that member, you'd score for useful work, but the need to put it in a queue first is not something the requirements engineer asked for.&lt;br /&gt;&lt;br /&gt;This measurement is a productivity ratio. Codey code slows down writing, reading, debugging and documenting and it provides lots of places for bugs to hide. Sometimes you need some of it, but people normally write a lot more than they need.&lt;br /&gt;&lt;br /&gt;Brevity=Quality&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-2157747582357806475?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/2157747582357806475/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2009/09/yet-another-project-health-check.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/2157747582357806475'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/2157747582357806475'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2009/09/yet-another-project-health-check.html' title='Yet another project health check'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-1578295174062480493</id><published>2009-09-28T02:35:00.001-07:00</published><updated>2009-09-28T04:09:17.223-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='darwinism'/><category scheme='http://www.blogger.com/atom/ns#' term='memes'/><category scheme='http://www.blogger.com/atom/ns#' term='dawkins'/><category scheme='http://www.blogger.com/atom/ns#' term='evolution'/><category scheme='http://www.blogger.com/atom/ns#' term='intelligence'/><category scheme='http://www.blogger.com/atom/ns#' term='intelligent design'/><category scheme='http://www.blogger.com/atom/ns#' term='consciousness'/><title type='text'>Creationism=Darwinism</title><content type='html'>It's ironic that Richard Dawkins can think of nothing more useful to do with his golden years than beat up soft targets like the "Intelligent Design" protagonists. Why ironic? Because his main contribution to the world is the theory that could leave creationists and Darwinists apparently agreeing with one another.&lt;br /&gt;&lt;br /&gt;Dawkins proposed that beliefs, habits, ideas, technologies, languages, religions, recipes, fashions, in fact, anything that goes on within or between human minds can be viewed as self interested, evolving life forms. It is undeniable that we can ask Darwinistic questions about such entities and the answers will tell us a lot about how common they are. For instance, there have been non-evangelical religions, but an awful lot of their followers now subscribe to an evangelical one instead. It's important to understand that we are talking about the success of the "memes" themselves, rather than that of the people they inhabit. So if you invented a great pizza but didn't make any money from it because every other pizzeria in town copied the recipe, then the recipe, viewed as a meme, would be doing well.&lt;br /&gt;&lt;br /&gt;If we go along with Dawkins about intelligence being a Darwinian process, then the &lt;span style="font-style: italic;"&gt;modus morons&lt;/span&gt; that all Darwinian processes could be considered intelligent presents itself. If it held, then it would be quite accurate to say that the animals etc came about through a process of "Intelligent Design" (albeit in more than 7 days, or is it 6?)  Discussions about whether things like the eye are too good or bad to have evolved or been designed, would be moot, because the two processes would be the same anyway. (BTW, it evolved on at least forty independant occasions.)&lt;br /&gt;&lt;br /&gt;But does the &lt;span style="font-style: italic;"&gt;modus morons&lt;/span&gt; hold? We can see that the biosphere solves temporary problems so as to keep itself in equilibrium, just like we do. If we are hungry, we eat. When the biosphere had too much oxygen it invented animals, and it can usually come up with a disease or predator to cope with any excess of a single species.  Human technology increases in both complexity and efficiency over time, as does natural technology. The results of human and natural development both exhibit ingenuity, but also dogmatism: our spines weren't really designed for walking upright but we have to make do, just like we have to make do with the Brits driving on the wrong side of the road.&lt;br /&gt;&lt;br /&gt;One has to compare like with like here. Earths and humans are different, but mainly because of how many there are and the extent to which they interact, compete and reproduce. The proper comparison is between the biosphere and human society as a whole. An individual in an evolving population has intentionality (even if only to slither to the warm end of the microscope slide) and an evolutionary prognosis which it can, to some extent, influence. The Earth, by contrast, sits in a constant environment and could do nothing about it if it did start hurtling into the sun or something. Earths don't reproduce or compete with one another either.&lt;br /&gt;&lt;br /&gt;Perhaps this explains why individual humans do so much talking about saving the environment but nothing significant ever happens at the global level. Individual humans have been trained by evolution to compete with one another, but there is no society of intelligent races competing to steer the climates of their worlds. If there were, the sensible ones would colonise more of the universe than us, so climactic common sense would evolve as a characteristic. But human society has not been through that evolutionary lesson, not even a tiny bit, so we just carry on doing what we have learned like the Earth carries on spinning around the sun.&lt;br /&gt;&lt;br /&gt;Is the biosphere conscious then? Does it have feelings? This is a pointless question because one can never test for it anyway. Even if we made a totally human-like robot that claimed to have personal experiences, some people would still accuse it of merely bouncing electrons around a circuit while the robot retorted that they were merely rubbing neurons together. There have been cultures that even denied women consciousness and many people find it convenient to imagine that laboratory animals don't feel much pain because they can't recognise themselves in mirrors for instance. So why ask a question that can never be settled?&lt;br /&gt;&lt;br /&gt;I don't think Dawkins can help with God's other jobs though: praying to the biosphere is unlikely to yield results and evolution is more interested in killing things than taking them to heaven afterwards. Nevertheless, the proposition that nature exhibits such ingenuity that an intelligent designer must be responsible, appears not to be so silly after all. I wouldn't be surprised, though, if half of the people gaining credibility here thought it meant that what humans have achieved is all mere chance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-1578295174062480493?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/1578295174062480493/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2009/09/creationismdarwinism.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/1578295174062480493'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/1578295174062480493'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2009/09/creationismdarwinism.html' title='Creationism=Darwinism'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-3932329251269980179</id><published>2009-09-26T20:15:00.001-07:00</published><updated>2009-09-27T20:46:32.201-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='android google cyanogen'/><title type='text'>Google gives up on open source</title><content type='html'>Empires come and go, but they all behave like empires.&lt;br /&gt;&lt;br /&gt;Android was supposed to be the open source phone. Anybody could contribute to it or modify it. That was the lie Google used to whip up some ideologically driven publicity for their most adventurous diversification ever. What diversification? Into phones? No. I mean into proprietory software.&lt;br /&gt;&lt;br /&gt;Google are now saying that the software you'll find on a typical Android phone is not just Android. Gmail, YouTube and a bunch of other things are Google proprietary apps and "Android" does not include them. Whatever you develop for Android, you may not distribute these apps.&lt;br /&gt;&lt;br /&gt;That's why they just sent a Cease and Desist letter to Cyanogen, who distributes his modified OS as a ROM that includes the latest Google apps in their original form. For some strange reason, Cyanogen seems to see this as wiping out his business overnight, and I'm eagerly awaiting some sign of flexibility from him.&lt;br /&gt;&lt;br /&gt;Right now, web servers are crashing under the volume of furious protests this is causing. One of the leading Android developers at Google (Jean Baptiste Queru) threw such a tantrum that he twittered an offer of his services to the competition, but he seems to have got over it within a few days. It must be a mistake for Google to throw away their open source street-cred like this, and it's another nail in the coffin for the illusion that the new Microsoft would behave any better than the old one.&lt;br /&gt;&lt;br /&gt;Assuming we never suffered from that illusion, it's also pathetic of Google to fail to get their brains around even this most elementary of open source monetisation puzzles. Those apps are already generating revenue in several ways. They drive traffic to the Google services they are named after, and land the customer with a huge 3G/UMTS bill.&lt;br /&gt;&lt;br /&gt;So why did they do it? Why did they stop somebody distributing apps to customers who already had those apps on their phone before they took the shrink wrap off? It's because they charge fees to the networks and phone companies for distributing the apps, who must have been complaining about Cyanogen getting the same priviledge for free. This complaint would not be grounded because Cyanogen does not benefit from 3G traffic.&lt;br /&gt;&lt;br /&gt;There's no question of charging anybody for the equivalent apps on your PC. Google don't deal with the PC manufacturers who pre-install Windows on your PC. Microsoft do, but Ubuntu don't. So who does Google seem to be aping here?&lt;br /&gt;&lt;br /&gt;And what does Android now allow that the rest don't? People can and do develop apps for Windows Mobile, PalmOS, etc. The only special thing about Android was access to the OS itself, but Google just made that dependent on the customer not wanting any software from Google, knowing damned well that they do. If that isn't monopoly abuse, what, pray tell, would be?&lt;br /&gt;&lt;br /&gt;If Google can't make up their mind which philosophy they want to follow, they won't be successful in either sense. They are forsaking a lot of fees for Android itself by leaving it open because they want the neo-socialist credentials to compete with Apple's cult image, but they just blew that for the sake of clawing a bit of proprietary revenue from apps that have never before been anything but interfaces to their services, and for which fees have never before been charged. The hypocrisy is almost as staggering as the stupidity of it.&lt;br /&gt;&lt;br /&gt;If the Android leadership really is so schizophrenic as to combine their first foray into open source development with their first attempt to charge licence fees, then a lot of people might want to avoid this confused market altogether, and Cyanogen would seem to be of that opinion.&lt;br /&gt;&lt;br /&gt;But he does still have the option of distributing his OS modifications without physically distributing the Google apps. After all, the customers have already got copies of them. All Cyanogen needs to do is suck the Google apps out of the original ROM, or install his modified OS without disturbing them. That ought to be possible, oughtn't it? The apps would still update themselves and the user could get entirely new apps by downloading the vendor's latest firmware, but presenting it to Cyanogen's sucker instead of installing it. There would be no legal challenge to this because Cyanogen would be not be giving the user any software that somebody else didn't already give them. I think even Google would be happy because they could still charge somebody for their apps, and that somebody couldn't complain about anyone else getting it for free. Everybody could be happy about Cyanogen's contribution to the attractiveness of any Android phone.&lt;br /&gt;&lt;br /&gt;But Google would still have lost everybody's trust, and they would still be a corporate contradiction.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-3932329251269980179?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/3932329251269980179/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2009/09/google-gives-up-pretending-to-be-nice.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/3932329251269980179'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/3932329251269980179'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2009/09/google-gives-up-pretending-to-be-nice.html' title='Google gives up on open source'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-4824524405053298437</id><published>2009-09-24T03:04:00.000-07:00</published><updated>2009-09-24T05:37:31.846-07:00</updated><title type='text'>Data duplication is the root of all evil.</title><content type='html'>It is astonishing that the style gurus never mention this issue. It's far more important than even basic modularisation, let alone OO. Think about the last bug you handled and ask yourself whether or not there was some duplicate data behind it. I reckon this accounts for at least 50% of bugs, probably more like 75%.&lt;br /&gt;&lt;br /&gt;People duplicate data because they want it in a different format, in a different machine, process or module, because they don't know it already exists or just because nobody told them it was wrong to do so. It's inevitable that the two copies will end up with different values sooner or later, resulting in an inconsistent system state. These bugs are normally "fixed" by inserting some overseen update of the redundant copy, but there are probably dozens of other places in the code where it needs to be updated, if not now then after a few development cycles. When the bug resurfaces the symptoms will look the same, and the developer will say "But I already fixed that one." No you didn't; you just postponed it.&lt;br /&gt;&lt;br /&gt;Once a redundancy exists, it's human nature to fail to maintain both copies. People normally expect one solution to one problem. When asked to write code to update the doofah, a programmer will declare himself finished when he's found the doofah and updated it. Even if he suspects that a second doofah might exist, how many more is he supposed to look for?&lt;br /&gt;&lt;br /&gt;Code managers should scour their code for any data redundancy and eliminate it. Any change that adds heap data should be audited thoroughly.  The heap should always be as small as possible. (In Haskell it's always zero.)&lt;br /&gt;&lt;br /&gt;If machine or process boundaries force duplication, then a low-level update mechanism should be in place with policies for connectivity failures, locking, etc (but how many of our process or machine boundaries are really forced?) Module boundaries don't force data duplication, they just encourage it. If you can't be bothered to write&lt;br /&gt;&lt;br /&gt;thismodule-&gt;parent-&gt;modulesmanager["theothermodule"]-&gt;doofahs[thismodule-&gt;parent-&gt;modulesmanager["theothermodule"]-&gt;getcurrentdoofah()]-&gt;asString()&lt;br /&gt;&lt;br /&gt;every time, just put a function to say that in your own module, but don't copy the doofah.&lt;br /&gt;&lt;br /&gt;Code redundancy can also be dangerous, but nowhere near as bad as duplicate data.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-4824524405053298437?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/4824524405053298437/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2009/09/duplicate-data-is-root-of-all-evil.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/4824524405053298437'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/4824524405053298437'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2009/09/duplicate-data-is-root-of-all-evil.html' title='Data duplication is the root of all evil.'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-1941967517153713323</id><published>2009-09-23T01:51:00.000-07:00</published><updated>2009-09-24T03:04:41.901-07:00</updated><title type='text'>The data furnace</title><content type='html'>Can't remember where I read this but I thought I'd regurgitate it anyway.&lt;br /&gt;&lt;br /&gt;They used to think that quantum mechanics placed a limit on how efficient computers could be. They even estimated how many orders of magnitude more electricity it costs to flip one of today's best switches than the theoretical best one anybody could ever make.&lt;br /&gt;&lt;br /&gt;But if you think about it, that doesn't really make sense. It's not QM, but thermodynamics that governs these things. Normally, things decay from ordered to disordered states, which means that if you set a bit to 1, it might be either 0 or 1 by the time you come back. That decay can happen by itself, but if you want to fix it back to 1, you'll accidentally randomise some other bit somewhere else in the universe. When machines "use" energy, they don't destroy it, they just let it escape their control. What they sell over the national grid is not just energy but energy where we want it.&lt;br /&gt;&lt;br /&gt;So if we want to make a computer of unlimited efficiency, we have to be careful about taking bits whose values we don't know and setting them to a value we do. But supposing we already had a bit lying around whose value was what we wanted, and we just swapped that with the bit we want to set. Before and after, we would have one known bit and one unknown one, so we wouldn't have changed the amount of order in the universe. We can do that for free.&lt;br /&gt;&lt;br /&gt;But where would all the known bits come from? We'd need a machine to make them. It would receive the bits we don't need anymore and set them to 1s and 0s ready to trade for our next batch of used data. It would certainly use energy and produce heat. Best keep it in the garden where it can cool down easily.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-1941967517153713323?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/1941967517153713323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2009/09/data-furnace.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/1941967517153713323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/1941967517153713323'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2009/09/data-furnace.html' title='The data furnace'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3200437250410140982.post-533318612479582495</id><published>2009-09-21T20:04:00.000-07:00</published><updated>2009-09-22T22:50:38.552-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='monads'/><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Monad tutorial in C++</title><content type='html'>&lt;p&gt;/* &lt;br/&gt;&lt;br /&gt;&lt;strong&gt;IT'S NOT ABOUT THE MONADS&lt;/strong&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Every Haskell newbie is expected to write a monad tutorial for homework so here's mine. I'm gonna do it in C++ because Haskell and monads are actually two different learning curves, and I assume that anybody who has even heard of Haskell probably learnt C++ while the eggs were boiling one day.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Everybody struggles to understand what monads are, but that's not really the important question. Monads are how you tell the compiler which &amp;gt;&amp;gt;= you want, so the first question is, what's &amp;gt;&amp;gt;=?&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&amp;gt;&amp;gt;= is a more flexible way of composing functions than the usual g(f(x).&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Seeing as g(f(x) is the default way of composing functions, it's easy to assume it's the only way,   and even when you spend half your life composing them differently, e.g. by checking return values, or logging, you don't necessarily realise that you are actually using an alternative notion of function composition.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;C++ code generally includes a lot of boring error checking and logging at least, which just makes the code tedious to read. It normally remains unfinished resulting in bugs. If you can factor out the way functions are composed, you can make it comprehensive, concise and pretty.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;So what we're trying to develop is a general scheme for writing alternative styles of function composition. On top of that, we'd like a slick way of choosing between them.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;So let's try that on the three examples of normal, careful and logged function composition. Logging is going to mean appending the functions' return values to a string - we assume the programmer can read his own code and figure out the order of execution without a log. This is more closely associated with composition than the usual idea of logging the function entries and exits. Nevertheless, the subject functions will end up with the option of logging on their own.&lt;br /&gt; &lt;br /&gt;&lt;/p&gt;&lt;p&gt;We'll make composer functions that take two subject functions and return an overall composed function according to these definitions of composition.&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;#include &amp;lt;string&amp;gt;&lt;br /&gt;#include &amp;lt;sstream&amp;gt;&lt;br /&gt;#include &amp;lt;iostream&amp;gt;&lt;br /&gt;using namespace std;&lt;br /&gt;string itoa(int i)&lt;br /&gt;{&lt;br /&gt; std::stringstream sin;&lt;br /&gt; sin &amp;lt;&amp;lt; i;&lt;br /&gt; return sin.str();&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;//Define some basic types to play with&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;typedef int A;&lt;br /&gt;typedef string B;&lt;br /&gt;struct C {int x; int y; C(){}; C(int x_, int y_)&lt;br /&gt; :x(x_),y(y_) {}};&lt;br /&gt;ostream &amp;amp; operator&amp;lt;&amp;lt;(ostream &amp;amp; os, C &amp;amp; c) &lt;br /&gt; {return os &amp;lt;&amp;lt; c.x &amp;lt;&amp;lt; "," &amp;lt;&amp;lt; c.y;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;//Define some types for the subject functions. We'll avoid using return values altogether because they're even less flexible in C++ than parameters&lt;br /&gt;&lt;/p&gt;&lt;p&gt;//Define the subject functions&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;char numnames[][6]={"zero","one","two","three"};&lt;br /&gt;void fn1(A &amp;amp; a, B &amp;amp; b)&lt;br /&gt;{&lt;br /&gt; b=numnames[a]; &lt;br /&gt; //crash on &amp;gt;3 is deliberate here&lt;br /&gt;}&lt;br /&gt;void gn1(B &amp;amp; b, C &amp;amp; c)&lt;br /&gt;{&lt;br /&gt; c.x=b.length();&lt;br /&gt; c.y=b[0];&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;//Now for the composers...&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;void composeNormal1(&lt;br /&gt; void (&amp;amp;f) (A&amp;amp;, B&amp;amp;), &lt;br /&gt; void (&amp;amp;g) (B&amp;amp;, C&amp;amp;), &lt;br /&gt; A &amp;amp; a, C&amp;amp; c)&lt;br /&gt;{&lt;br /&gt; B b;&lt;br /&gt; f(a,b);&lt;br /&gt; g(b,c);&lt;br /&gt;}&lt;br /&gt;void testNormal1() &lt;br /&gt;{&lt;br /&gt; C c; int a = 3;&lt;br /&gt; composeNormal1(fn1, gn1, a, c); &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;composeCareful is not going to work on the above types because they don't return errors at all, so lets fix that&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;void fe1(A &amp;amp; a, B &amp;amp; b, bool &amp;amp; e)&lt;br /&gt;{&lt;br /&gt; e = (a&amp;lt;=3); &lt;br /&gt; if (e) b=numnames[a];&lt;br /&gt;}&lt;br /&gt;void ge1(B &amp;amp; b, C &amp;amp; c, bool &amp;amp; e)&lt;br /&gt;{&lt;br /&gt; c.x=b.length();&lt;br /&gt; e = (c.x&amp;gt;0);&lt;br /&gt; if (e) c.y=b[0];&lt;br /&gt;}&lt;br /&gt;void composeCareful1(&lt;br /&gt; void (&amp;amp;f) (A&amp;amp;, B&amp;amp;, bool&amp;amp;), &lt;br /&gt; void (&amp;amp;g) (B&amp;amp;, C&amp;amp;, bool&amp;amp;), &lt;br /&gt; A &amp;amp; a, C &amp;amp; c, bool &amp;amp; e)&lt;br /&gt;{&lt;br /&gt; B b;&lt;br /&gt; f(a,b,e);&lt;br /&gt; if (e) g(b,c,e);        &lt;br /&gt;}&lt;br /&gt;void testCareful1() &lt;br /&gt;{&lt;br /&gt; C c; A a=3; bool e;&lt;br /&gt; composeCareful1(fe1, ge1, a, c, e); &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;For logging, the functions themselves don't need to change because compose is going to handle the logging, but the composed function has to return the log. We would like to be able to take the composed function and pass it to another compose as either the first or the second parameter, so that means compose should take functions that output logs. Anyway, the log is supposed to grow so the log so far has to come from somewhere. The only place it can come from is the only place it can go to, i.e. the output of functions, whether composed or not.&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;void fl1(A &amp;amp; a, B &amp;amp; b, string &amp;amp; log)&lt;br /&gt;{&lt;br /&gt; b=numnames[a]; &lt;br /&gt;}&lt;br /&gt;void gl1(B &amp;amp; b, C &amp;amp; c, string &amp;amp; log)&lt;br /&gt;{&lt;br /&gt; c.x=b.length();&lt;br /&gt; c.y=b[0];&lt;br /&gt;}&lt;br /&gt;string toString(int i) {return itoa(i);}&lt;br /&gt;string toString(string s) {return s;}&lt;br /&gt;void composeLogged1(&lt;br /&gt; void (&amp;amp;f) (A&amp;amp;, B&amp;amp;, string&amp;amp;), &lt;br /&gt; void (&amp;amp;g) (B&amp;amp;, C&amp;amp;, string&amp;amp;), &lt;br /&gt; A &amp;amp; a, C &amp;amp; c, string &amp;amp; log)&lt;br /&gt;{&lt;br /&gt; B b;&lt;br /&gt; f(a,b,log);&lt;br /&gt; log += "Got:";&lt;br /&gt; log += toString(b) + ";";&lt;br /&gt; string l_;&lt;br /&gt; g(b,c,l_);      &lt;br /&gt; log += l_;      &lt;br /&gt;}&lt;br /&gt;void testLogged1() &lt;br /&gt;{&lt;br /&gt; C c; A a=3; string log;&lt;br /&gt; composeLogged1(fl1, gl1, a, c, log); &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;OK, we've achieved something: we've factored out the logging and checking code into our own composer functions so we don't have to write those details all over the code. But we still have to write composeLogged, composeCareful or composeNormal everywhere, it'll be hard to change between them, and we have to write different versions of the subject functions according to which type of compose we're using. So let's try to improve on that. &lt;br /&gt; &lt;br /&gt;&lt;/p&gt;&lt;p&gt;An OO programmer might be thinking that compose could be an overridable in some yet-to-be-invented class hierarchy. Depending on which subclass we make, we'd get one of the composing styles. It would need the subject functions passing to it somehow, but we can't use an overridable for that because the C++ override system only overrides functions with the same parameters, wheras our subject functions have different types for the different composing styles. The same applies to the compose operation itself. Actually, there's no use for overrides here at all, but overloading global functions might work. &lt;br /&gt;&lt;/p&gt;&lt;p&gt;In fact, the composeLogged, composeCareful or composeNormal could all have the same name because they all take different auxillary parameters (nothing, string and bool) but this is just an accident of the example we're using. We can tighten that up while we write the overloads.&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;struct HoldNormal {};&lt;br /&gt;void compose2(&lt;br /&gt; void (&amp;amp;f) (A&amp;amp;, B&amp;amp;), &lt;br /&gt; void (&amp;amp;g) (B&amp;amp;, C&amp;amp;), &lt;br /&gt; A &amp;amp; a, C &amp;amp; c, HoldNormal&amp;amp; z) ;&lt;br /&gt;&lt;br /&gt;struct HoldCareful {bool e;HoldCareful(bool e_):e(e_){}};&lt;br /&gt;void compose2(&lt;br /&gt; void (&amp;amp;f) (A&amp;amp;, B&amp;amp;, HoldCareful&amp;amp;), &lt;br /&gt; void (&amp;amp;g) (B&amp;amp;, C&amp;amp;, HoldCareful&amp;amp;), &lt;br /&gt; A &amp;amp; a, C &amp;amp; c, HoldCareful&amp;amp; e)&lt;br /&gt;{&lt;br /&gt; B b;&lt;br /&gt; f(a,b,e);&lt;br /&gt; if (e.e) g(b,c,e);      &lt;br /&gt;}&lt;br /&gt;void fe2(A &amp;amp; a, B &amp;amp; b, HoldCareful &amp;amp; e)&lt;br /&gt;{&lt;br /&gt; e.e = (a&amp;lt;=3); &lt;br /&gt; if (e.e) b=numnames[a];&lt;br /&gt;} //etc for the other subject functions&lt;br /&gt;&lt;br /&gt;struct HoldLogged {string log;};&lt;br /&gt;void compose2(&lt;br /&gt; void (&amp;amp;f) (A&amp;amp;, B&amp;amp;, HoldLogged&amp;amp;), &lt;br /&gt; void (&amp;amp;g) (B&amp;amp;, C&amp;amp;, HoldLogged&amp;amp;), &lt;br /&gt; A &amp;amp; a, C &amp;amp; c, HoldLogged &amp;amp; log)&lt;br /&gt;{&lt;br /&gt; B b;&lt;br /&gt; f(a,b,log);&lt;br /&gt; log.log += "Got:";&lt;br /&gt; log.log += toString(b) + ";";&lt;br /&gt; HoldLogged l_;&lt;br /&gt; g(b,c,l_);      &lt;br /&gt; log.log += l_.log;&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;Sooner of later we have to extend this to types other than int, string and that struct, which we do in C++ using templates. &lt;br/&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;void fl3(A &amp;amp; a, B &amp;amp; b, HoldLogged &amp;amp; log)&lt;br /&gt;{&lt;br /&gt; b=numnames[a]; &lt;br /&gt;}&lt;br /&gt;void gl3(B &amp;amp; b, C &amp;amp; c, HoldLogged &amp;amp; log)&lt;br /&gt;{&lt;br /&gt; c.x=b.length();&lt;br /&gt; c.y=b[0];&lt;br /&gt;}&lt;br /&gt;template&amp;lt;class AA, class BB, class CC&amp;gt; &lt;br /&gt;void compose3(&lt;br /&gt; void (&amp;amp;f) (AA&amp;amp;, BB&amp;amp;, HoldLogged&amp;amp;), &lt;br /&gt; void (&amp;amp;g) (BB&amp;amp;, CC&amp;amp;, HoldLogged&amp;amp;), &lt;br /&gt; A &amp;amp; a, C &amp;amp; c, HoldLogged &amp;amp; log)&lt;br /&gt;{&lt;br /&gt; B b;&lt;br /&gt; f(a,b,log);&lt;br /&gt; log.log += "Got:";&lt;br /&gt; log.log += toString(b) + ";";&lt;br /&gt; HoldLogged l_;&lt;br /&gt; g(b,c,l_);      &lt;br /&gt; log.log += l_.log;&lt;br /&gt;}&lt;br /&gt;void testLogged3() &lt;br /&gt;{&lt;br /&gt; C c; A a=3; HoldLogged log;&lt;br /&gt; compose3(fl3, gl3, a, c, log); &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;That was a bit messy. We had to make another HoldLogged in the middle of compose just to call the second function, and we've actually got three different log streams coalescing in debatable ways: the one passed to the combined function, and the ones generated by the two functions. In fact, we've had extra bools and strings from the start. All we really want is to concatenate the logs from the two subject functions with compose's two penn'orth in between and return that. Maybe a better way is to have the subject functions create the HoldLoggeds. This would mean you can't pass the Hold/string/bool or whatever into a subject function, but you probably don't need to anyway. If there had been an error in the first function, we wouldn't be calling the second at all, so the second doesn't need to be told that the first was OK. For logging, the second function can start with a blank log and compose puts the strings together. So it might work.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In that case, the Holds made by the subject functions should be returned as normal return values because then they'll be created on the stack and cleaned up easily. &lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;HoldLogged fl4(A &amp;amp; a, B &amp;amp; b)&lt;br /&gt;{&lt;br /&gt; b=numnames[a]; &lt;br /&gt; return HoldLogged();&lt;br /&gt;}&lt;br /&gt;HoldLogged gl4(B &amp;amp; b, C &amp;amp; c)&lt;br /&gt;{&lt;br /&gt; c.x=b.length();&lt;br /&gt; c.y=b[0];&lt;br /&gt; return HoldLogged();&lt;br /&gt;}&lt;br /&gt;template&amp;lt;class AA, class BB, class CC&amp;gt; &lt;br /&gt;HoldLogged compose4(&lt;br /&gt; HoldLogged (&amp;amp;f) (AA&amp;amp;, BB&amp;amp;), &lt;br /&gt; HoldLogged (&amp;amp;g) (BB&amp;amp;, CC&amp;amp;), &lt;br /&gt; A &amp;amp; a, C &amp;amp; c)&lt;br /&gt;{&lt;br /&gt; B b; &lt;br /&gt; HoldLogged log = f(a,b);&lt;br /&gt; log.log += "Got:";&lt;br /&gt; log.log += toString(b) + ";";&lt;br /&gt; log.log += g(b,c).log;&lt;br /&gt; return log;&lt;br /&gt;}&lt;br /&gt;void testLogged4() &lt;br /&gt;{&lt;br /&gt; C c; A a=3; &lt;br /&gt; compose4(fl4, gl4, a, c); &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;Something quite interesting has just happened though: compose no longer cares about A! We can call f ourselves and just pass its result (B) to compose along with the Hold it made. (Later we'll put that back so it looks nice from the test function, but we can do the checking and logging logic without A.)&lt;br/&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;template&amp;lt;class BB, class CC&amp;gt; &lt;br /&gt;HoldLogged compose5(&lt;br /&gt; HoldLogged &amp;amp; logf, B &amp;amp; b, &lt;br /&gt; HoldLogged (&amp;amp;g) (BB&amp;amp;, CC&amp;amp;), &lt;br /&gt; C &amp;amp; c)&lt;br /&gt;{&lt;br /&gt; logf.log += "Got:";&lt;br /&gt; logf.log += toString(b) + ";";&lt;br /&gt; logf.log += g(b,c).log;&lt;br /&gt; return logf; &lt;br /&gt; //its a bit naughty that we're returning the same log we &lt;br /&gt; //received, but it doesn't matter at this stage&lt;br /&gt;}&lt;br /&gt;HoldLogged fl5(A &amp;amp; a, B &amp;amp; b)&lt;br /&gt;{&lt;br /&gt; b=numnames[a]; &lt;br /&gt; return HoldLogged();&lt;br /&gt;}&lt;br /&gt;HoldLogged gl5(B &amp;amp; b, C &amp;amp; c)&lt;br /&gt;{&lt;br /&gt; c.x=b.length();&lt;br /&gt; c.y=b[0];&lt;br /&gt; return HoldLogged();&lt;br /&gt;}&lt;br /&gt;void testLogged5() &lt;br /&gt;{&lt;br /&gt; B b; C c; A a=3; &lt;br /&gt; HoldLogged log = fl5(a,b);&lt;br /&gt; compose5(log, b, gl5, c); &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;Its a bit of a faff pulling b and the log around seperately, but we could stash one inside the other.&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;template&amp;lt;class T&amp;gt; &lt;br /&gt;struct Logged  : HoldLogged&lt;br /&gt;{&lt;br /&gt; T t;&lt;br /&gt; Logged(T t_):t(t_){}&lt;br /&gt;};&lt;br /&gt; &lt;br /&gt;template&amp;lt;class BB, class CC&amp;gt; &lt;br /&gt;HoldLogged compose6(&lt;br /&gt; Logged&amp;lt;BB&amp;gt; logf, &lt;br /&gt; HoldLogged (&amp;amp;g) (BB&amp;amp;, CC&amp;amp;), &lt;br /&gt; C &amp;amp; c)&lt;br /&gt;{&lt;br /&gt; logf.log += "Got:";&lt;br /&gt; logf.log += toString(logf.t) + ";";&lt;br /&gt; HoldLogged log2 = g(logf.t,c);                  &lt;br /&gt; logf.log += log2.log;&lt;br /&gt; return logf;&lt;br /&gt;}&lt;br /&gt;Logged&amp;lt;B&amp;gt; fl6(A &amp;amp; a)&lt;br /&gt;{&lt;br /&gt; return Logged&amp;lt;B&amp;gt; (numnames[a]);&lt;br /&gt;}&lt;br /&gt;HoldLogged gl6(B &amp;amp; b, C &amp;amp; c)&lt;br /&gt;{&lt;br /&gt; c.x=b.length();&lt;br /&gt; c.y=b[0];&lt;br /&gt; return HoldLogged();&lt;br /&gt;}&lt;br /&gt;void testLogged6() &lt;br /&gt;{&lt;br /&gt; C c; A a=3; &lt;br /&gt; compose6(fl6(a), gl6, c); &lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;But we want f and g to look the same.&lt;br/&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;Logged&amp;lt;B&amp;gt; fl7(A &amp;amp; a)&lt;br /&gt;{&lt;br /&gt; return Logged&amp;lt;B&amp;gt;(numnames[a]);&lt;br /&gt;}&lt;br /&gt;Logged&amp;lt;C&amp;gt; gl7(B &amp;amp; b)&lt;br /&gt;{&lt;br /&gt; return Logged&amp;lt;C&amp;gt;(C(b.length(), b[0]));&lt;br /&gt;}&lt;br /&gt;template&amp;lt;class B, class C&amp;gt;&lt;br /&gt;Logged&amp;lt;C&amp;gt; compose(&lt;br /&gt; Logged&amp;lt;B&amp;gt; logf, &lt;br /&gt; Logged&amp;lt;C&amp;gt; (&amp;amp;g) (B &amp;amp;))&lt;br /&gt;{&lt;br /&gt; Logged&amp;lt;C&amp;gt; logg = g(logf.t);                     &lt;br /&gt; logg.log = (logf.log &lt;br /&gt;  + "Got:" + toString(logf.t) + ";"  &lt;br /&gt;  + logg.log);&lt;br /&gt; return logg;&lt;br /&gt;}&lt;br /&gt;void testLogged7() &lt;br /&gt;{&lt;br /&gt; A a=3; &lt;br /&gt; Logged&amp;lt;C&amp;gt; log = compose(fl7(a), gl7);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;What &amp;gt;&amp;gt;= really does is return a function that acts on A. To do that in C++ we'll have to faff around &lt;br /&gt;like this:&lt;br/&gt;&lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;template&amp;lt;class AA, class BB, class CC&amp;gt;&lt;br /&gt;struct Logger&lt;br /&gt;{&lt;br /&gt; Logged&amp;lt;B&amp;gt; (&amp;amp;f) (A &amp;amp;);&lt;br /&gt; Logged&amp;lt;C&amp;gt; (&amp;amp;g) (B &amp;amp;);&lt;br /&gt; Logger(&lt;br /&gt;  Logged&amp;lt;B&amp;gt; (&amp;amp;f_) (A &amp;amp;), &lt;br /&gt;  Logged&amp;lt;C&amp;gt; (&amp;amp;g_) (B &amp;amp;))&lt;br /&gt;  :f(f_),g(g_){}&lt;br /&gt; Logged&amp;lt;C&amp;gt; operator()(A &amp;amp; a) &lt;br /&gt; {return compose(f(a),g);}&lt;br /&gt;};&lt;br /&gt;void testLogged8() &lt;br /&gt;{&lt;br /&gt; A a=3;&lt;br /&gt; Logger&amp;lt;A,B,C&amp;gt; logger(fl7, gl7) ;&lt;br /&gt; Logged&amp;lt;C&amp;gt; lc = logger(a);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;Now we've got something presentable for the logger, we should try to extend it to the careful and normal composers. &lt;br /&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;template&amp;lt;class T&amp;gt; &lt;br /&gt;struct Careful &lt;br /&gt;{&lt;br /&gt; bool b;&lt;br /&gt; T t;&lt;br /&gt; Careful(T t_):t(t_),b(true){}&lt;br /&gt; Careful():b(false){}&lt;br /&gt;};&lt;br /&gt;Careful&amp;lt;B&amp;gt; fc7(A &amp;amp; a)&lt;br /&gt;{&lt;br /&gt; return (a&amp;gt;3) &lt;br /&gt;  ? Careful&amp;lt;B&amp;gt;() &lt;br /&gt;  : Careful&amp;lt;B&amp;gt;(numnames[a]);&lt;br /&gt;}&lt;br /&gt;Careful&amp;lt;C&amp;gt; gc7(B &amp;amp; b)&lt;br /&gt;{&lt;br /&gt; return (b.length()==0) &lt;br /&gt;  ? Careful&amp;lt;C&amp;gt;() &lt;br /&gt;  : Careful&amp;lt;C&amp;gt;(C(b.length(), b[0]));&lt;br /&gt;}&lt;br /&gt;template&amp;lt;class B, class C&amp;gt;&lt;br /&gt;Careful&amp;lt;C&amp;gt; compose(&lt;br /&gt; Careful&amp;lt;B&amp;gt; carefulf, &lt;br /&gt; Careful&amp;lt;C&amp;gt; (&amp;amp;g) (B &amp;amp;))&lt;br /&gt;{&lt;br /&gt; if (!carefulf.b) return Careful&amp;lt;C&amp;gt;();&lt;br /&gt; return g(carefulf.t);                   &lt;br /&gt;}&lt;br /&gt;void testCareful7() &lt;br /&gt;{&lt;br /&gt; A a=3; &lt;br /&gt; Careful&amp;lt;C&amp;gt; careful = compose(fc7(a), gc7);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/////////////&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;template&amp;lt;class T&amp;gt; &lt;br /&gt;struct Normal &lt;br /&gt;{&lt;br /&gt; T t;&lt;br /&gt; Normal(T t_):t(t_){}&lt;br /&gt;};&lt;br /&gt;Normal&amp;lt;B&amp;gt; fn7(A &amp;amp; a)&lt;br /&gt;{&lt;br /&gt; return Normal&amp;lt;B&amp;gt;(numnames[a]);&lt;br /&gt;}&lt;br /&gt;Normal&amp;lt;C&amp;gt; gn7(B &amp;amp; b)&lt;br /&gt;{&lt;br /&gt; return Normal&amp;lt;C&amp;gt;(C(b.length(), b[0]));&lt;br /&gt;}&lt;br /&gt;template&amp;lt;class B, class C&amp;gt;&lt;br /&gt;Normal&amp;lt;C&amp;gt; compose(&lt;br /&gt; Normal&amp;lt;B&amp;gt; normalf, &lt;br /&gt; Normal&amp;lt;C&amp;gt; (&amp;amp;g) (B &amp;amp;))&lt;br /&gt;{&lt;br /&gt; return g(normalf.t);                    &lt;br /&gt;}&lt;br /&gt;void testNormal() &lt;br /&gt;{&lt;br /&gt; A a=3; &lt;br /&gt; Normal&amp;lt;C&amp;gt; answer = compose&amp;lt;B,C&amp;gt;(fn7(a), gn7);&lt;br /&gt; //The main thing about this line is that compose &lt;br /&gt; //is chosen based on the kind of monad we wrap &lt;br /&gt; //the function returns in&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;/*&lt;br/&gt;&lt;br /&gt;Normal, Logged and Careful are monads, but they're not exactly the same as their Haskell equivalents. A monad's purpose is to say which &amp;gt;&amp;gt;= we want and carry around the return values of the subject functions plus any other info that &amp;gt;&amp;gt;= needs. The constructors of these monads are like return in Haskell.&lt;br /&gt; &lt;br /&gt;&lt;/p&gt;&lt;p&gt;In Haskell we can just say return and let the type system figure out which monad to make, but C++ doesn't allow overloading based on return type. We could try to do that here but it would only introduce more red herrings like the Logger.&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;But even in Haskell, the subject functions can't be completely independant of the style of composition. MonadPlus makes this independance a little bit better, but nevertheless, the reason you would want to deploy something like the careful (Maybe) monad is that you know your functions will want to fail for reasons best known to themselves.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;There are also monad transformers, which add monad functionality to existing monads. If you always write your functions to use the Id monad (normal above), you can decide later to add other monads to it using something like the MaybeT transformer. This will save some rewriting.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;*/&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;br /&gt;int main(int argc, char* argv[])&lt;br /&gt;{&lt;br /&gt; A a=3; &lt;br /&gt; Normal&amp;lt;C&amp;gt; answer1 = compose&amp;lt;B,C&amp;gt;(fn7(a), gn7);&lt;br /&gt; cout &amp;lt;&amp;lt; "Normal test: Param="&amp;lt;&amp;lt;a&amp;lt;&amp;lt;&lt;br /&gt;  " Answer="&amp;lt;&amp;lt;answer1.t&amp;lt;&amp;lt;endl;&lt;br /&gt; Careful&amp;lt;C&amp;gt; answer2 = compose&amp;lt;B,C&amp;gt;(fc7(a), gc7);&lt;br /&gt; cout &amp;lt;&amp;lt; "Careful test: Param="&amp;lt;&amp;lt;a&amp;lt;&amp;lt;&lt;br /&gt;  " Success="&amp;lt;&amp;lt;answer2.b&amp;lt;&amp;lt;&lt;br /&gt;  " Answer="&amp;lt;&amp;lt;answer2.t&amp;lt;&amp;lt;endl;&lt;br /&gt; a=4;&lt;br /&gt; Careful&amp;lt;C&amp;gt; answer3 = compose&amp;lt;B,C&amp;gt;(fc7(a), gc7);&lt;br /&gt; cout &amp;lt;&amp;lt; "Careful bad test: Param="&amp;lt;&amp;lt;a&amp;lt;&amp;lt;&lt;br /&gt;  " Success="&amp;lt;&amp;lt;answer3.b&amp;lt;&amp;lt;&lt;br /&gt;  " Answer="&amp;lt;&amp;lt;answer3.t&amp;lt;&amp;lt;endl;&lt;br /&gt; a=2;&lt;br /&gt; Logged&amp;lt;C&amp;gt; answer4 = compose&amp;lt;B,C&amp;gt;(fl7(a), gl7);&lt;br /&gt; cout &amp;lt;&amp;lt; "Logged test: Param="&amp;lt;&amp;lt;a&amp;lt;&amp;lt;&lt;br /&gt;  " Log="&amp;lt;&amp;lt;answer4.log&amp;lt;&amp;lt;&lt;br /&gt;  " Answer="&amp;lt;&amp;lt;answer4.t&amp;lt;&amp;lt;endl;&lt;br /&gt; return 0;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3200437250410140982-533318612479582495?l=nuerd.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://nuerd.blogspot.com/feeds/533318612479582495/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://nuerd.blogspot.com/2009/09/monad-tutorial-in-c.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/533318612479582495'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3200437250410140982/posts/default/533318612479582495'/><link rel='alternate' type='text/html' href='http://nuerd.blogspot.com/2009/09/monad-tutorial-in-c.html' title='Monad tutorial in C++'/><author><name>adrian</name><uri>http://www.blogger.com/profile/10716494902915423540</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry></feed>
