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 ): "(#pycallNow 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.)" 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 ) )
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.