#!/usr/bin/python
#######################################################################################################################
# Continuation passing based, functionally pure I/O in python!
# Author: Gregory Masseau.
#######################################################################################################################
# This is the main program we're going to run, stored as a thunk so we don't have to worry about what's defined. Skim
# it, and you might get some idea what we're going to do.
main = lambda:chain( (inject("Hi. Enter a string, I will repeat it twice in uppercase.\n\nEntry: ")) (prntc)(discard)
, (input) (mul(2))(uc) (interject((prntc("Result:"))(discard))) (prnt) (discard)
, (inject("\nBye.\n"))(prnt) (end("Success."))
)
# Now lets define all the functions we need to handle this sort of code.
#######################################################################################################################
# These functions are combinators which transform a function into a curried version of itself. This means a function that
# was previously called using syntax line 'func(x,y)' must now be called using the syntax 'func(x)(y)' to achieve the
# same effect. The advantage is that it can also be called like 'func(x)' to partially apply it, returning a function
# that recieves the remaining variables.
def curried_2args(f): return lambda x:lambda y: f(x,y)
def curried_3args(f): return lambda x:lambda y:lambda z: f(x,y,z)
def curried_4args(f): return lambda x:lambda y:lambda z:lambda w: f(x,y,z,w)
#----------------------------------------------------------------------------------------------------------------------
# These functions are combinators that modify a function (which should already be curried) to behave in a continuation
# passing style, taking the continuation as an additional argument and continuing with it (passing what would ordinarily
# be the return value) to it instaed of returning it as they ordinarily would -- remember, in this style of programming
# only the last function ever returns.
def continuationpasser_thunk(f): return lambda c: c(f())
def continuationpasser_1arg(f): return lambda x:lambda c: c(f(x))
def continuationpasser_2args(f): return lambda x:lambda y:lambda c: c(f(x)(y))
def continuationpasser_3args(f): return lambda x:lambda y:lambda z:lambda c: c(f(x)(y)(z))
def continuationpasser_4args(f): return lambda x:lambda y:lambda z:lambda w:lambda c: c(f(x)(y)(z)(w))
#----------------------------------------------------------------------------------------------------------------------
# This turns a value into a value expecting a continuation.
def inject(x): return lambda c: c(x)
# This evaluates it's argument and calls it's continuation without it.
def discard(x): return lambda c: c
# This is basically shortand for (discard)(inject x).
def restart(x): return lambda y:lambda c: c(x)
# This is fun. This takes another sequence of commands in CPS format, and runs those, before passing it's argument to
# it's continuation.
def interject(f): return lambda x:lambda c: c(x) if f else c(x)
# This just ends with the exit status/message 'v', expecting no continuation.
def end(v): return lambda x: (inject(v))(id)
# Since inject just passes it's argument to it's continuation, it can be used as a no op in CPS code blocks.
def noop(x): return inject(x)
#----------------------------------------------------------------------------------------------------------------------
# This is kind of like (but not quite, I don't think) a monadic bind, taking a thunk and a function and returning a thunk that does the first function to the result of the thunk, returning the result of the second.
def chained(x,y): return lambda: y(x())
# This is just the previous function extended to operate on n thunks, where 1 <>
def chain(*fs): return reduce(chained,fs)
#----------------------------------------------------------------------------------------------------------------------
# The identity function. End a CPS function sequence with it to return a value into normal code.
def id(x): return x
#######################################################################################################################
# These are some trivial math and I/O functions, appropriately curried and lifted to work in our CPS model.
# Adds two objects implementing +.
@continuationpasser_2args
@curried_2args
def add(x,y): return y+x
#----------------------------------------------------------------------------------------------------------------------
# Multiplies two objects implementing *.
@continuationpasser_2args
@curried_2args
def mul(x,y): return y*x
#----------------------------------------------------------------------------------------------------------------------
# Increments a number.
@continuationpasser_1arg
def inc(x): return 1+x
#----------------------------------------------------------------------------------------------------------------------
# Takes input from stdin.
@continuationpasser_thunk
def input(): return raw_input()
#----------------------------------------------------------------------------------------------------------------------
@continuationpasser_1arg
# Prints a value to stdout.
def prnt(s):
print str(s)
return True
#----------------------------------------------------------------------------------------------------------------------
# Prints a value to stdout, printing no newline after.
@continuationpasser_1arg
def prntc(s):
print str(s),
return True
#----------------------------------------------------------------------------------------------------------------------
# Returns a string in uppercase.
@continuationpasser_1arg
def uc(s): return s.upper()
#######################################################################################################################
# Now we call main, executing our program.
main()
# Instead of calling main as above, just uncommenting this will also have a similar effect, though it's a little less
# pure since it is techincally three statements when used that way.
#(inject("Hi. Enter a string, I will repeat it twice in uppercase.\n\nEntry: "))(prntc)(discard)
#(input)(mul(2))(uc)(interject((prntc("Result:"))(discard)))(prnt)(discard)
#(inject("\nBye.\n"))(prnt)(end("Success."))
# The following is equally valid, if somewhat less readable, as well as about as pure as this gets right now.
#(inject("Hi. Enter a string, I will repeat it twice in uppercase.\n\nEntry:"))(prntc)(discard)(input)(mul(2))(uc)(interject((prntc("Result:"))(discard)))(prnt)(discard) (inject("\nBye.\n"))(prnt)(end("Success."))
# These are some trivial math and I/O functions, appropriately curried and lifted to work in our CPS model.
# Adds two objects implementing +.
@continuationpasser_2args
@curried_2args
def add(x,y): return y+x
#----------------------------------------------------------------------------------------------------------------------
# Multiplies two objects implementing *.
@continuationpasser_2args
@curried_2args
def mul(x,y): return y*x
#----------------------------------------------------------------------------------------------------------------------
# Increments a number.
@continuationpasser_1arg
def inc(x): return 1+x
#----------------------------------------------------------------------------------------------------------------------
# Takes input from stdin.
@continuationpasser_thunk
def input(): return raw_input()
#----------------------------------------------------------------------------------------------------------------------
@continuationpasser_1arg
# Prints a value to stdout.
def prnt(s):
print str(s)
return True
#----------------------------------------------------------------------------------------------------------------------
# Prints a value to stdout, printing no newline after.
@continuationpasser_1arg
def prntc(s):
print str(s),
return True
#----------------------------------------------------------------------------------------------------------------------
# Returns a string in uppercase.
@continuationpasser_1arg
def uc(s): return s.upper()
#######################################################################################################################
# Now we call main, executing our program.
main()
# Instead of calling main as above, just uncommenting this will also have a similar effect, though it's a little less
# pure since it is techincally three statements when used that way.
#(inject("Hi. Enter a string, I will repeat it twice in uppercase.\n\nEntry: "))(prntc)(discard)
#(input)(mul(2))(uc)(interject((prntc("Result:"))(discard)))(prnt)(discard)
#(inject("\nBye.\n"))(prnt)(end("Success."))
# The following is equally valid, if somewhat less readable, as well as about as pure as this gets right now.
#(inject("Hi. Enter a string, I will repeat it twice in uppercase.\n\nEntry:"))(prntc)(discard)(input)(mul(2))(uc)(interject((prntc("Result:"))(discard)))(prnt)(discard) (inject("\nBye.\n"))(prnt)(end("Success."))