Thursday, March 30, 2006
Python Monad
It looks like I've got a Python monad working in the Simple Lambda Evaluator:
E:\work\lambda>python elle.py ------------------------------------------------------------------------------ ((#pyseq((#pycall "print")((#cons "hello")((#cons "world")#nil))))(\x.((#pycall "print")((#cons "wow")#nil)))) ------------------------------------------------------------------------------ hello world wow ['#', None]So far I've only tested output - input will have to wait a while. The significant changes made to the evaluator include:
- A new cell type (#) containing a raw python value;
- Builtin functions #pyseq and #pycall. The #pycall function accepts two arguments: the name of the Python function, and a list of arguments;
- Additional builtin functions #cons etc... that aren't strictly required, but ensure that the handling of lists by elle and the evaluator is consistent.
_car = [ '\\', 'c', [ '@', [ 'V', 'c' ], [ '\\', 'h', [ '\\', 't', [ 'V', 'h' ] ] ] ] ]
_cdr = [ '\\', 'c', [ '@', [ 'V', 'c' ], [ '\\', 'h', [ '\\', 't', [ 'V', 't' ] ] ] ] ]
_true = [ '\\', 'x', [ '\\', 'y', [ 'V', 'x' ] ] ]
_false = [ '\\', 'x', [ '\\', 'y', [ 'V', 'y' ] ] ]
_nil = [ '\\', 'c', _true ]
_nullq = [ '\\', 'c', [ '@', [ 'V', 'c' ], [ '\\', 'h', [ '\\', 't', _false ] ] ] ]
def nullq( cell ):
"""Return True if at the end of the list, else False
"""
return bool( _arg( [ '@', [ '@', [ '@', _nullq, cell ], [ 'N', 1 ] ], [ 'N', 0 ] ], 'N' ) )
def car( cell ):
"First element of a list"
root = [ '@', _car, cell ]
reduce( root )
return root
def pyval( cell ):
"Extract python value from cell"
if cell[ 0 ] in 'N"#':
return cell[ 1 ]
else:
raise "Bad python cell '%s'" % cell[ 0 ]
def _pycall( stack ):
"(#pycall )"
if len( stack ) < 3:
raise "Not enough arguments"
fn = _arg( stack[ -2 ][ 2 ], '"' )
el = stack[ -3 ][ 2 ]
args = []
while not nullq( el ):
args.append( pyval( car( el ) ) )
el = cdr( el )
stack[ -3 ][ 0 ] = "#"
stack[ -3 ][ 1 ] = pyfunc( fn )( *tuple( args ) )
Now that is not too bad - the function nullq() (testing for the end of the list) builds a cell representation of an expression, and uses the evaluator to calculate a (Python) boolean (True if at the end of the list). A similar trick is used by car() and cdr(). The pyval() function is also quite simple: if the cell can be interpreted by Python its value gets extracted.
In essence, all that the builtin implementations of #car, #cdr etc... do is enforce a lambda expression. The underlying evaluation does not take any short cuts - the lambda expressions are still evaluated.