{-
The PingPong program simulates client-server communication.
Here the client sends a request to the server that says "ping",
and the client answers "pong".
-}
use console


{-
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 for to call ping n times. As for evaluates as needed,
# all is used to evaluate all the terms. The recursive version
# (without for) is left as an exercise to the reader.
def functpong = fun (int n, Stdout con) -> return()
{
[for i in 1...n:
functping(con) ->
console!con.Puts("pong").
].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 for 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 invalidping = fun (int n, Stdout con) -> yield1(?), done()
{
fun() -> c(): yield1(c). -> callback
[for i in 1...n:
console!con.Puts("ping") ->
callback().
].all ->
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 (Note: an empty
# continuation ->| defaults to returning its arguments). When
# ping is done, return.
def invalidpong = fun (int n, Stdout con) -> return()
{
invalidping(n, con) -> pingcallback
# for comprehension syntax:
# [for index = initialvalue; condition; nextindex
# {itemvalue}] -- here itemvalue block is omitted
[for handle = pingcallback;;(
console!con.Puts("pong") ->
handle() ->
| # update handle or... return() # done )].all | return() # done if n=0 } {-
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 for 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()
{
[for i in 1...n:
console!con.Puts("ping") ->
callback().
].all ->
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 [for {
console!con.Puts("pong") ->
resume &handle ->
| # implicitely update handle or... return() # done }].all | return() # done if n=0 } # Call pong with 15 bounces def Main
{
Main():
pong(15, Stdout()).
}