
Constructing some code today in Python, using some functional-style coding idioms, I found myself wondering if there would be any real benefit to using a monad-based implementation (i.e. other than to demonstrate that it can be done). The application that sparked this line of thought was a simple filter to trim comments and whitespace out of an XML document. I ended up with a simple state-machine driven filter, thus: [[ # Strip comments from XML strings def stripXmlComments(x): # State table for stripping XML comments stStripXmlComments = \ { 0: match1('<',(1,stateFilterHold),(0,stateFilterPass)) , 1: match1('!',(2,stateFilterHold),(0,stateFilterPass)) , 2: match1('-',(3,stateFilterHold),(0,stateFilterPass)) , 3: match1('-',(4,stateFilterDrop),(0,stateFilterPass)) , 4: match1('-',(5,stateFilterDrop),(4,stateFilterDrop)) , 5: match1('-',(6,stateFilterDrop),(4,stateFilterDrop)) , 6: match1('>',(0,stateFilterDrop),(4,stateFilterDrop)) } return stateFilter(stStripXmlComments,x) # Simple state machine driven filter # # The state table is a dictionary indexed by state values, where the # initial state is 0, and each entry is a function that accepts a next # symbol and returns a pair of (next state, action), where action is # one of 'stateFilterPass', 'stateFilterDrop', 'stateFilterHold'. # stateFilterHold means that the disposition will be determined later. # # The result is an iterator that returns elements from the filtered # subsequence of the supplied sequence. # def stateFilter(stable,seq): queue = [] state = 0 for symbol in seq: (state,action) = stable[state](symbol) (queue,emit) = action(queue,symbol) for e in emit: yield e return def stateFilterPass(q,n): return ([],q+[n]) def stateFilterDrop(q,n): return ([],[]) def stateFilterHold(q,n): return (q+[n],[]) # State transition function to match the specified symbol and return # 'eqval' if matched, otherwise 'neval' def match1(sym,eqval,neval): def m(sym,eqval,neval,next): if next==sym: return eqval return neval return curry(m,sym,eqval,neval) def curry(func, *args): """ Curry multiple arguments: See: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/229472 """ def curried(*args2): args2 = args + args2 return func(*args2) return curried ]] and a test case: [[ def testFilter02(self): fullstr = " <!--def--> " trimstr = list(stripXmlComments(fullstr)) expstr = list(" ") assert trimstr==expstr, \ "stripSpaces, expected:\n"+expstr+"\nFound:\n"+trimstr ]] In thinking about this implementation, it seemed to me that this employed patterns characteristic of a monadic type: each entry in the state table (in this case, an instance of match1, a curried function) is like a step in a monadic computation, updating the monadic value and also returning some value. What I can't quite visualize is if the code in Python would actually look any better if it were implemented with a monadic type, as one might readily choose for a Haskell implementation. Or would there be no real benefit? I have noticed that, while I like to use functional idioms in some of my Python code, and the Python language is easily able to support these (even some lazy evaluation, courtesy of generators), that the code doesn't always look as clean as its Haskell equivalent. In Haskell, composition and currying are fundamental patterns and are directly supported by the syntax. In Python, one has to work harder to achieve these (e.g. the "curry" function above seems rather convoluted to me, for such a fundamental notion). Thoughts? Comments? #g -- Graham Klyne For email: http://www.ninebynine.org/#Contact

On 2/3/06, Graham Klyne
I have noticed that, while I like to use functional idioms in some of my Python code, and the Python language is easily able to support these (even some lazy evaluation, courtesy of generators), that the code doesn't always look as clean as its Haskell equivalent. In Haskell, composition and currying are fundamental patterns and are directly supported by the syntax. In Python, one has to work harder to achieve these (e.g. the "curry" function above seems rather convoluted to me, for such a fundamental notion).
Thoughts? Comments?
Hi Graham, You might be interested in my `functional` package. It includes tools for composition, partial application, flip, foldl, foldr, scanl and scanr, all coded as C extensions for speed. I initially wrote the code to scratch my own more-functional-programming-in-Python itch; maybe it can help you out in that department as well : ) http://oakwinter.com/code/functional/ Feedback always appreciated. Thanks, Collin Winter
participants (2)
-
Collin Winter
-
Graham Klyne