<!-- The PingPong program simulates client-server communication. Here the client sends a request to the server that says "ping", and the client answers "pong". --> <!-- First let us see a functional version of ping and pong to get used to the concept. The client simply calls ping n times, and puts "pong" when it returns. --> # functping: functional version of ping # Prints "ping" and returns to pong def functping = fun (Stdout con -> return() { console!con.Puts("ping") }) # functpong: functional version of pong # Uses unfold to call ping n times. As unfold evaluates as needed, # count is used to evaluate all the terms. The recursive version # (without unfold) is left as an exercise to the reader. def functpong = fun (int n, Stdout con -> return() { unfold (i in 1...n: ping(con) -> console!con.Puts("pong"). ).count # do all }) <!-- Now to the real thing. This time it is ping that stops after it has produced n "ping"s, the client just keeps requesting the server until the server says done. This example illustrates first-class continuations. However I am not sure that I want first-class continuations in dodo, as the code can get quite confusing. This is why I did not define a type for them. --> # ping with first-class continuation # Note: this is not valid dodo syntax. Comment this part to run. # Because the parameter of yield1 is a continuation, we use ? as # type since dodo has no type defined for continuations. Here # callback() is the equivalent of call/cc in other languages. # As before, we use unfold to put "ping" n times. The callback # call sends the control back to the caller while saving the # current stack, so that the computation can continue when # ping regains control. def ping = fun (int n, Stdout con -> yield1(?), done() { fun (-> c(): yield1(c). ) -> callback unfold (i in 1...n: console!con.Puts("ping") -> callback(). ).count -> done() }) # pong with first-class continuation # Send a request to ping and receive a handle. Then put "pong" # and give the control back to ping using the handle. The value # of the handle is updated for next iteration. When ping is # done, return. def pong = fun (int n, Stdout con -> return() { unfold (handle = ping(n, con);; console!con.Puts("pong") -> handle() -> | # update handle or... return() # done ).count # do all }) <!-- The final version uses the yield and resume constructs to replace first-class continuations. It is otherwise identical to the version with first-class continuations. --> # ping with yield # Use unfold to put n "ping"s, allowing pong to run after each. # The callback call sends the control back to the caller while # the yield type saves the current stack, so that the computa- # tion can continue when ping regains control. def ping = fun (int n, Stdout con -> callback yield(), done() { unfold (i in 1...n: console!con.Puts("ping") -> callback(). ).count -> done() }) # pong with resume # Send a request to ping and get the hand. Then put "pong" # and give the control back to ping using resume. When # ping is done, return. def pong = fun (int n, Stdout con -> return() { ping(n, con) -> $$handle unfold (true: console!con.Puts("pong") -> resume $$handle -> | # update handle or... return(). # done ).count # do all }) # Call pong with 15 bounces def Main { Main(): pong(15, Stdout()). }