| Advanced Control FlowThus far, the only control flow mechanism you've been shown was the labelconstruct for creating labeled scopes. But this one construct was powerful enough to express early returns from functions, early breaks from loops, and also (though we haven't shown it) early jumps to the next loop iteration. Each of the above functionality has traditionally been a separate keyword and language feature in other languages, but they're all expressible with just thelabelconstruct in Stanza. It is a powerful and general mechanism. In actuality, Stanza only has a single control flow mechanism, called targetable coroutines. The labelconstruct is just a common usage pattern for them. In this chapter we'll learn about the other common usage pattern of coroutines: attempts and failures, exception handlers, and generators. At the very end, we'll show you the general coroutine construct, along with some examples demonstrating their use. First Class Labeled ScopesHere is a function that finds the smallest power of two that is greater or equal to the given argument, n. defn min-pow-2 (n:Int) :label<Int> return :
 var x = 1
 while true :
 if x >= n : return(x)
 x = x * 2
 fatal("Unreachable")
 Let's try it out. defn main () :defn test (n:Int) :
 println("The minimum power of 2 greater or equal to %_ is %_." %
 [n, min-pow-2(n)])
 test(10)
 test(100)
 test(1000)
 test(10000)
 test(100000)
 test(1000000)
 
 main()
 Compiling and running the above prints out The minimum power of 2 greater or equal to 10 is 16.The minimum power of 2 greater or equal to 100 is 128.
 The minimum power of 2 greater or equal to 1000 is 1024.
 The minimum power of 2 greater or equal to 10000 is 16384.
 The minimum power of 2 greater or equal to 100000 is 131072.
 The minimum power of 2 greater or equal to 1000000 is 1048576.
 Pulling Out the BodyThat's fairly standard so far. But now let's pull out the body of the while loop into a separate function, called pow-2?. It accepts an argument,x, that is the current number being tested, an argument,n, as the limit we're trying to reach, and also a function calledreturn, which we'll explain later. defn pow-2? (x:Int, n:Int, return:Int -> Void) -> Int :if x >= n : return(x)
 else : x * 2
 pow-2?first checks whetherxis greater or equal ton, and callsreturnwithxif it is. Otherwise it returns the next value ofxtotest, which isx * 2.
 We now update the min-pow-2function to callpow-2?. defn min-pow-2 (n:Int) :label<Int> return :
 var x = 1
 while true :
 x = pow-2?(x, n, return)
 fatal("Unreachable")
 Compile and run the program to verify that it still works. What is happening here!? We've somehow passed the exit function out of min-pow-2and intopow-2?. Then whenpow-2?calledreturn, it returned not frompow-2?, but frommin-pow-2!. Let's review the definition of the labelconstruct. Here is its general form. label<T> exit :body
 The labelconstruct requires the type it returns,T, the name of the exit function,exit, and the body to execute,body.labelcreates an exit function of typeT -> Voidwith the nameexit, and then executesbody. Ifbodynever calls the exit function then the result ofbodyis returned bylabel. Ifbodycalls the exit function thenlabelimmediately returns the argument passed to the exit function. The return typeVoidfor the exit function indicates that it doesn't return to its caller. There is nothing in the description of labelpreventing us from passing out the exit function, so the call toreturninpow-2?is simply causing thelabelconstruct to returnx, which is then returned bymin-pow-2. Storing the Exit FunctionWe can even store the exit function in a variable if we like. Here's a global variable var RETURN: Int -> Void
 into which we will store the exit function. Thus the exit function will no longer be passed in to pow-2?as an argument. defn pow-2? (x:Int, n:Int) -> Int :if x >= n : RETURN(x)
 else : x * 2
 It will instead be stored in the global variable before pow-2?is called. defn min-pow-2 (n:Int) :label<Int> return :
 RETURN = return
 var x = 1
 while true :
 x = pow-2?(x, n)
 fatal("Unreachable")
 Compile and verify that the program still works as before. Here you're starting to see just how flexible the labelconstruct really is. Storing the exit function seems like a strange thing to want to do but keep it in the back of your mind as we talk about the other constructs. Dynamic WindOne of the issues that accompanies having a powerful control flow mechanism is that in a sequence of expressions, evaluating the first expression does not guarantee that the last one will be evaluated. f()g()
 h()
 For example, in the above sequence, even after freturns, there is no guarantee thathwill be called. Ifgcalls an exit function thenhwill be skipped entirely. Let us suppose that we do not want to pass in the limit, n, as an argument topow-2?. We would like to keep it stored in a global variable calledLIMITand set it to the appropriate value only during the call topow-2?. At all other times,LIMITshould retain its initial value of0. Here is an initial attempt. var LIMIT: Int = 0var RETURN: Int -> Void
 
 defn pow-2? (x:Int) -> Int :
 if x >= LIMIT : RETURN(x)
 else : x * 2
 
 defn min-pow-2 (n:Int) :
 label<Int> return :
 RETURN = return
 var x = 1
 while true :
 val old-limit = LIMIT
 LIMIT = n
 x = pow-2?(x)
 LIMIT = old-limit
 fatal("Unreachable")
 
 defn main () :
 defn test (n:Int) :
 println("The minimum power of 2 greater or equal to %_ is %_." %
 [n, min-pow-2(n)])
 test(10)
 test(100)
 test(1000)
 test(10000)
 test(100000)
 test(1000000)
 
 main()
 println("After main, LIMIT is %_." % [LIMIT])
 Printing and compiling the above prints out The minimum power of 2 greater or equal to 10 is 16.The minimum power of 2 greater or equal to 100 is 128.
 The minimum power of 2 greater or equal to 1000 is 1024.
 The minimum power of 2 greater or equal to 10000 is 16384.
 The minimum power of 2 greater or equal to 100000 is 131072.
 The minimum power of 2 greater or equal to 1000000 is 1048576.
 After main, LIMIT is 1000000.
 So min-pow-2is working correctly, butLIMITis not being restored back to its original value. What is happening? Well the call topow-2?in LIMIT = nx = pow-2?(x)
 LIMIT = old-limit
 may call the exit function, return. If that happens, then thelabelconstruct immediately returns and the lastLIMIT = old-limitexpression is never evaluated. Stanza provides the special function dynamic-windto handle these situations. It allows you to surround a body of code between some wind in and wind out code. The wind in code is guaranteed to execute whenever the control flow enters the body, and the wind out code is guaranteed to execute whenever the control flow exits the body. Here is how it's used. defn min-pow-2 (n:Int) :label<Int> return :
 RETURN = return
 var x = 1
 while true :
 val old-limit = LIMIT
 dynamic-wind(
 fn () :
 LIMIT = n
 fn () :
 x = pow-2?(x)
 fn (final) :
 LIMIT = old-limit)
 fatal("Unreachable")
 The wind in, body, and wind out code is given to dynamic-windas three anonymous functions. Thefinalargument for the wind out code is a boolean value that indicates whether it is guaranteed to be the last time the wind out code is called. In this example,finalwill always betrue. Now compiling and running the program again prints out The minimum power of 2 greater or equal to 10 is 16.The minimum power of 2 greater or equal to 100 is 128.
 The minimum power of 2 greater or equal to 1000 is 1024.
 The minimum power of 2 greater or equal to 10000 is 16384.
 The minimum power of 2 greater or equal to 100000 is 131072.
 The minimum power of 2 greater or equal to 1000000 is 1048576.
 After main, LIMIT is 0.
 indicating the LIMITis properly being reset to its original value. Dynamically Scoped VariablesIn the above example, we say that LIMITis being used as a dynamically scoped variable. This is a common pattern and Stanza provides a syntactic shorthand for our call todynamic-wind. Here is the min-pow-2function written using thelet-varshorthand. defn min-pow-2 (n:Int) :label<Int> return :
 RETURN = return
 var x = 1
 while true :
 let-var LIMIT = n :
 x = pow-2?(x)
 fatal("Unreachable")
 The general form of let-varis let-var x = v :body
 It temporarily sets the xvariable to the valuevbefore executingbody.xis restored to its previous value afterbodyis finished executing. Attempts and FailuresAttempts and failures are syntactic sugar for another use case of targetable coroutines that operate very similarly to labeled scopes. Here is an example. defn read-letter (xs:Seq<Char>) -> Char :if not letter?(peek(xs)) : fail()
 next(xs)
 
 defn read-digit (xs:Seq<Char>) -> Char :
 if not digit?(peek(xs)) : fail()
 next(xs)
 
 defn read-all (xs:Collection<Char>) :
 val xs-seq = to-seq(xs)
 while not empty?(xs-seq) :
 attempt :
 println("Read letter: %_" % [read-letter(xs-seq)])
 else attempt :
 println("Read digit: %_" % [read-digit(xs-seq)])
 else :
 println("Read something else: %~" % [next(xs-seq)])
 
 read-all("42 is the answer.")
 Compiling the above prints out Read digit: 4Read digit: 2
 Read something else: ' '
 Read letter: i
 Read letter: s
 Read something else: ' '
 Read letter: t
 Read letter: h
 Read letter: e
 Read something else: ' '
 Read letter: a
 Read letter: n
 Read letter: s
 Read letter: w
 Read letter: e
 Read letter: r
 Read something else: '.'
 The function read-allcallsread-letterandread-digitin anattemptblock. If the block evaluates without ever callingfailthen the result of the block is returned byattempt. If the block callsfail, thenattemptimmediately returns the result of evaluating the code in theelsebranch. Example: S-Expression ParserHere is an example of using attemptandfailto program a simple s-expression parser. An s-expression, in this case, will be defined as either a positive integer,
a symbol consisting of letters,
or a list containing more s-expressions.
 Overall StructureHere is the basic structure of the parser. defn parse-sexp (sexp:String) -> List :val chars = to-seq(sexp)
 
 defn eat-while (pred?: Char -> True|False) -> String :
 ...
 
 defn eat-whitespace () -> False :
 ... calls eat-while ...
 
 defn parse-symbol () -> Symbol :
 ... calls eat-while ...
 
 defn parse-number () -> Int :
 ... calls eat-while ...
 
 defn parse-sequence () -> List :
 ... calls eat-whitespace, parse-symbol, parse-number, and parse-list
 
 defn parse-list () -> List :
 ... calls parse-list ...
 
 parse-sequence()
 ...
 parse-sexp is given a string, and returns a list of s-expressions. Upon entering the function, we ask to view the string as a sequence of characters, chars.eat-whileandeat-whitespaceare helper functions.parse-symbol,parse-number,parse-sequence, andparse-listare mutually recursive functions that parse symbols, numbers, sequences of s-expressions, and lists of s-expressions. Helper FunctionsThe eat-while, andeat-whitespacefunctions are helper functions for reading fromchars.eat-whiletakes a predicate function,pred?, and eats characters fromcharsas long aspred?returnstrue. It returns a string containing the eaten characters.eat-whitespaceeats all leading spaces inchars. Here are their definitions. defn eat-while (pred?: Char -> True|False) -> String :string-join(take-while(pred?, chars))
 
 defn eat-whitespace () -> False :
 eat-while({_ == ' '})
 false
 Parsing SymbolsThe parse-symbolfunction eats and returns the next symbol fromchars. If the next character incharsis not a letter, thenparse-symbolfails. defn parse-symbol () -> Symbol :if not letter?(peek(chars)) : fail()
 to-symbol(eat-while(letter?))
 Parsing NumbersThe parse-numberfunction eats and returns the next positive integer fromchars. If the next character incharsis not a digit, or if the number cannot be represented in 32 bits, thenparse-numberfails. defn parse-number () -> Int :if not digit?(peek(chars)) : fail()
 val x = to-int(eat-while(digit?))
 if x is-not Int : fail()
 x as Int
 Parsing SequencesThe parse-sequencefunction reads as many s-expressions as possible by callingparse-symbol,parse-number, andparse-listrepeatedly. defn parse-sequence () -> List :eat-whitespace()
 if empty?(chars) :
 List()
 else :
 attempt : cons(parse-symbol(), parse-sequence())
 else attempt : cons(parse-number(), parse-sequence())
 else attempt : cons(parse-list(), parse-sequence())
 else : List()
 Notice the use of attemptto first try parsing a symbol, and then if that fails to then try parsing a number, followed by trying to parse a list. Parsing ListsThe parse-listfunction eats and returns the next list fromchars. A list is simply a sequence of s-expressions surrounded by()characters. If the next character is not an opening parenthesis thenparse-listfails. defn parse-list () -> List :   if peek(chars) != '(' : fail()
 next(chars)
 val items = parse-sequence()
 if empty?(chars) : fatal("Unclosed opening parenthesis.")
 else if peek(chars) == ')' : (next(chars), items)
 else : fatal("Expected closing parenthesis but got %~." % [next(chars)])
 DriverFinally, to start off the function, we attempt to read as many s-expressions as possible from charsusingparse-sequence. val items = parse-sequence()if empty?(chars) : items
 else : fatal("Unexpected character: %~." % [next(chars)])
 ListingHere is the complete definition of parse-sexp. defn parse-sexp (sexp:String) :val chars = to-seq(sexp)
 
 defn eat-while (pred?: Char -> True|False) -> String :
 string-join(take-while(pred?, chars))
 
 defn eat-whitespace () -> False :
 eat-while({_ == ' '})
 false
 
 defn parse-symbol () -> Symbol :
 if not letter?(peek(chars)) : fail()
 to-symbol(eat-while(letter?))
 
 defn parse-number () -> Int :
 if not digit?(peek(chars)) : fail()
 val x = to-int(eat-while(digit?))
 if x is-not Int : fail()
 x as Int
 
 defn parse-sequence () -> List :
 eat-whitespace()
 if empty?(chars) :
 List()
 else :
 attempt : cons(parse-symbol(), parse-sequence())
 else attempt : cons(parse-number(), parse-sequence())
 else attempt : cons(parse-list(), parse-sequence())
 else : List()
 
 defn parse-list () -> List :
 if peek(chars) != '(' : fail()
 next(chars)
 val items = parse-sequence()
 if empty?(chars) : fatal("Unclosed opening parenthesis.")
 else if peek(chars) == ')' : (next(chars), items)
 else : fatal("Expected closing parenthesis but got %~." % [next(chars)])
 
 val items = parse-sequence()
 if empty?(chars) : items
 else : fatal("Unexpected character: %~." % [next(chars)])
 Trying it OutLet's try it out on the following string. do(println, parse-sexp("This (is) (commonly (called an (S) (Expression)))"))
 When compiled and ran it prints out This(is)
 (commonly (called an (S) (Expression)))
 Unveiling The InternalsThe attemptconstruct is just syntactic sugar for a function call. attempt : conseqelse : alt
 is equivalent to with-attempt(fn () : conseq
 fn () : alt)
 As an exercise, try and implement your own with-attemptfunction by using thelabelconstruct. Exception HandlingOur s-expression parser from the previous example fails when called with invalid input (though with very nice error messages). Here is what happens if we forget a closing parenthesis at the end. do(println, parse-sexp("This (is) (commonly (called an (S) (Expression))"))
 Compiling and running the above prints out FATAL ERROR: Unclosed opening parenthesis.at test.stanza:39.25
 at test.stanza:32.29
 at core/core.stanza:3725.13
 at core/core.stanza:847.16
 at core/core.stanza:3724.14
 ...
 But what if we cannot guarantee that the input is correct? Suppose we want users to type an arbitrary string into the terminal and print the parsed s-expression if it's well formed, or else ask them to try again if it's not. A potential solution would be to write another function called sexp?that returnstrueorfalsedepending on whether its argument is a well formed string. But checking whether an s-expression is well formed is almost as much work as parsing it, so that's an inefficient solution. Stanza provides us a mechanism for handling this called exceptions. Exception ObjectsThe first step is to declare our own Exceptiontypes, one for each type of error the parser can encounter. defstruct UnclosedParenthesis <: Exceptiondefmethod print (o:OutputStream, e:UnclosedParenthesis) :
 print(o, "Unclosed opening parenthesis.")
 
 defstruct UnmatchedParenthesis <: Exception : (char:Char)
 defmethod print (o:OutputStream, e:UnmatchedParenthesis) :
 print(o, "Expected closing parenthesis but got %~." % [char(e)])
 
 defstruct UnexpectedCharacter <: Exception : (char:Char)
 defmethod print (o:OutputStream, e:UnexpectedCharacter) :
 print(o, "Unexpected character: %~." % [char(e)])
 There are three different errors that are detected by our parser.  The string is missing a closing parenthesis at the end.
We are currently reading a list and encountered a strange character.
We've read as many s-expressions as possible and there is a strange character left over.
 Throwing ExceptionsThe next step is to change the calls to fatalto calls tothrowwith our newly definedExceptionobjects. defn parse-sexp (sexp:String) :
 ...
 
 defn parse-list () -> List :
 ...
 if empty?(chars) : throw(UnclosedParenthesis())
 else if peek(chars) == ')' : (next(chars), items)
 else : throw(UnmatchedParenthesis(next(chars)))
 
 val items = parse-sequence()
 if empty?(chars) : items
 else : throw(UnexpectedCharacter(next(chars)))
 Catching ExceptionsThe final step is to catch the thrown exceptions. We can decide which types of exceptions to catch, and which not to. In this example, we'll assume that the string doesn't contain any strange characters and catch only the unclosed parenthesis error. try :do(println, parse-sexp("This (is) (commonly (called an (S) (Expression))"))
 catch (e:UnclosedParenthesis) :
 println("You forgot to close an opening parenthesis. Please try again.")
 Compiling and running the program now prints out You forgot to close an opening parenthesis. Please try again.
 Here is the general form of the tryconstruct. try :body
 catch (e:ExceptionA) :
 a-handler
 catch (e:ExceptionB) :
 b-handler
 ...
 The tryconstruct evaluates the givenbodyafter installing the given exception handlers. Ifbodyis evaluated without ever callingthrowthen its result is returned bytry. Ifbodycallsthrowwith someExceptionobject, thentryimmediately searches for the first exception handler that can accept theExceptionobject and returns the result of evaluating that handler. GeneratorsThe control flow constructs you've been introduced to so far, labeled scopes, attempts and failures, and exceptions, have all served the purpose of leaving a block of code. Generators are the first control flow construct you will learn capable of resuming a block of code.  Here is a generator that yields the first three positive integers. val xs:Seq<Int> = generate<Int> :println("Yielding One")
 yield(1)
 println("Yielding Two")
 yield(2)
 println("Yielding Three")
 yield(3)
 Notice that the generateconstruct returns aSeq. Let's try printing out the items in the sequence. println("The first item in xs is")println(next(xs))
 println("The second item in xs is")
 println(next(xs))
 println("The third item in xs is")
 println(next(xs))
 Compiling and running the above prints out The first item in xs isYielding One
 1
 The second item in xs is
 Yielding Two
 2
 The third item in xs is
 Yielding Three
 3
 The Ability to ResumeIt's worth paying attention to the order in which the messages are printed out. The snippet println("The first item in xs is")println(next(xs))
 by itself, prints out The first item in xs isYielding One
 1
 Thus the call to next(xs)causes control to enter the block of code in thegenerateconstruct. The message"Yielding One"is printed out, and then the call toyield(1)leaves thegenerateconstruct and1is the return value ofnext(xs). The next snippet println("The second item in xs is")println(next(xs))
 prints out The second item in xs isYielding Two
 2
 Thus the call to next(xs)causes control to re-enter the blockgenerateconstruct, resuming from just after the first call toyield. The message"Yielding Two"is printed out, and then the call toyield(2)leaves thegenerateconstruct once again and2is the return value of the second call tonext(xs). The last snippet println("The third item in xs is")println(next(xs))
 prints out The third item in xs isYielding Three
 3
 Similarly, the call to next(xs)resumes the block in thegenerateconstruct from just after the second call toyield. The message"Yielding Three"is printed out, and then the call toyield(3)leaves thegenerateconstruct once again and3is the return value of the third call tonext(xs). Thus the generateconstruct provides a very convenient way of creating a lazily constructed sequence. General FormHere is the general form of the generateconstruct. generate<T> :body
 generatereturns aSeq<T>by lazily executing the givenbodyin a scope containing the generation functions,yieldandbreak.
 yieldis of typeT -> Falseand its argument becomes an element in the generatedSeq. Execution of the generate block pauses atyield, and is resumed on the next call tonexton the sequence.
 breakis both of type() -> VoidandT -> Void. If no argument is given tobreak, then execution of the generate block ends here and marks the end of the generated sequence. If an argument is given tobreak, then that element is first yielded before the generate block is ended.
 If the generated type, T, is not explicitly provided, then it is assumed to be?by default. Example: Flattening a TupleIn this example, we'll determine whether two tuples contain the same elements as each other if we lay out their elements in depth-first order. For example, the tuple [[1] [2 [3]] [[4 5] 6]]
 contains the elements 1,2,3,4,5,6once laid out in depth-first order. Here's the most straightforward way of doing this. We'll write a function called flattenthat returns aVectorcontaining a tuple's elements in depth-first order. defn flatten (x:Tuple) -> Vector :val v = Vector<?>()
 defn loop (x) :
 match(x) :
 (x:Tuple) : do(loop, x)
 (x) : add(v, x)
 loop(x)
 v
 Let's try it out. println(flatten([[1] [2 [3]] [[4 5] 6]]))
 Compiling and running the above prints out [1 2 3 4 5 6]
 To check whether two tuples contain the same elements, we can just flatten each of them and then compare the elements. defn same-elements? (a:Tuple, b:Tuple) :if all?(equal?, flatten(a), flatten(b)) :
 println("%_ and %_ have the same elements." % [a, b])
 else :
 println("%_ and %_ have different elements." % [a, b])
 Let's test it out on the following tuples. same-elements?([[1] [2 [3]] [[4 5] 6]]
 [1 [[2 3 4] [5]] [6]])
 
 same-elements?(
 [[1] [2 [3]] [[4 5] 6]]
 [[[0] 2] [3 [4 5]] 6])
 Compiling and running the above prints out [[1] [2 [3]] [[4 5] 6]] and [1 [[2 3 4] [5]] [6]] have the same elements.[[1] [2 [3]] [[4 5] 6]] and [[[0] 2] [3 [4 5]] 6] have different elements.
 Notice though, that in both cases, we computed a full flattening of both tuples before checking to see whether they are equal. This is obviously inefficient in the second case since we can tell they are clearly different just by examining their first element. How do we avoid computing the full flattening? The solution is to lazily compute the flattening. Let's change flattento use thegenerateconstruct to lazily compute the flattened tuples. To track how much of the tuples are being flattened let's also add a print statement. defn flatten (x:Tuple) -> Seq :val index = to-seq(0 to false)
 generate :
 defn loop (x) :
 match(x) :
 (x:Tuple) :
 do(loop, x)
 (x) :
 println("Yielding Item %_" % [next(index)])
 yield(x)
 loop(x)
 Compiling and running the program again prints out Yielding Item 0Yielding Item 0
 Yielding Item 1
 Yielding Item 1
 Yielding Item 2
 Yielding Item 2
 Yielding Item 3
 Yielding Item 3
 Yielding Item 4
 Yielding Item 4
 Yielding Item 5
 Yielding Item 5
 [[1] [2 [3]] [[4 5] 6]] and [1 [[2 3 4] [5]] [6]] have the same elements.
 Yielding Item 0
 Yielding Item 0
 [[1] [2 [3]] [[4 5] 6]] and [[[0] 2] [3 [4 5]] 6] have different elements.
 Thus the results are the same as before, and you can see that, for the second comparison, both generators (one for each tuple) are only computing up to the first element. CoroutinesThe label,attempt,try, andgenerateconstructs are all specific usage patterns of Stanza's targetable coroutine system. Here we'll show you how to use the coroutine system in its full generality. It is rare in daily programming to encounter a problem that requires a use of coroutines that isn't already handled by one of the special case constructs. But for implementing libraries and frameworks that make heavy use of concurrency and non-standard control flow, coroutines may be indispensable. Sending Things OutHere is the function that will represent our coroutine body. defn my-process (co:Coroutine<Int,String>, a:Int) -> String :println("Passing out Timon")
 suspend(co, "Timon")
 println("Passing out and")
 suspend(co, "and")
 println("Passing out Pumbaa")
 suspend(co, "Pumbaa")
 println("Coroutine is done")
 "Done"
 The type Coroutine<Int,String>represents a coroutine for which integers are sent into the coroutine, and for which strings are sent back from the coroutine. The function for sending values out of the coroutine issuspend. Let's now create our coroutine object and resume it a few times. println("Create coroutine")val co = Coroutine<Int,String>(my-process)
 
 println("\nResume with 42")
 val x = resume(co, 42)
 println("Got back x = %_" % [x])
 
 println("\nResume with 43")
 val y = resume(co, 43)
 println("Got back y = %_" % [y])
 
 println("\nResume with 44")
 val z = resume(co, 44)
 println("Got back z = %_" % [z])
 
 println("\nResume with 45")
 val w = resume(co, 45)
 println("Got back w = %_" % [w])
 Notice that resumeis called with integers. When the above is compiled and ran it prints out Create coroutine
 Resume with 42
 Passing out Timon
 Got back x = Timon
 
 Resume with 43
 Passing out and
 Got back y = and
 
 Resume with 44
 Passing out Pumbaa
 Got back z = Pumbaa
 
 Resume with 45
 Coroutine is done
 Got back w = Done
 Thus suspendacts much likeyielddid for thegenerateconstruct, andresumeacts much likenextdid. This is no accident of course. Thegenerateconstruct is implemented in terms ofsuspendandresumeunderneath. Breaking Things OffIn addition to suspend, a function calledbreakcan also be used to send values out of a coroutine. The difference is that a call tobreakcannot later be resumed. Let's change our my-processfunction to send out"Pumbaa"withbreakinstead ofsuspend. defn my-process (co:Coroutine<Int,String>, a:Int) -> String :println("Passing out Timon")
 suspend(co, "Timon")
 println("Passing out and")
 suspend(co, "and")
 println("Passing out Pumbaa")
 break(co, "Pumbaa")
 println("Coroutine is done")
 "Done"
 Compiling and running the program again prints out Create coroutine
 Resume with 42
 Passing out Timon
 Got back x = Timon
 
 Resume with 43
 Passing out and
 Got back y = and
 
 Resume with 44
 Passing out Pumbaa
 Got back z = Pumbaa
 
 Resume with 45
 FATAL ERROR: Cannot resume coroutine. Coroutine is already closed.
 at core/core.stanza:984.13
 at core/core.stanza:862.16
 at core/core.stanza:897.40
 at core/core.stanza:862.16
 at test.stanza:31.8
 The coroutine is closed after the call to break, and thus our call toresumefails. Sending Things InThe obvious unanswered question now is: what is happening with the 42,43,44, and45values thatresumeis being called with? To answer that, let's update ourmy-processfunction to print out the return values ofsuspend(and change the call tobreakback intosuspend). defn my-process (co:Coroutine<Int,String>, a:Int) -> String :println("Came in a = %_" % [a])
 println("Passing out Timon")
 val b = suspend(co, "Timon")
 
 println("Came in b = %_" % [b])
 println("Passing out and")
 val c = suspend(co, "and")
 
 println("Came in c = %_" % [c])
 println("Passing out Pumbaa")
 val d = suspend(co, "Pumbaa")
 
 println("Came in d = %_" % [d])
 println("Coroutine is done")
 "Done"
 
 println("Create coroutine")
 val co = Coroutine<Int,String>(my-process)
 
 println("\nResume with 42")
 val x = resume(co, 42)
 println("Got back x = %_" % [x])
 
 println("\nResume with 43")
 val y = resume(co, 43)
 println("Got back y = %_" % [y])
 
 println("\nResume with 44")
 val z = resume(co, 44)
 println("Got back z = %_" % [z])
 
 println("\nResume with 45")
 val w = resume(co, 45)
 println("Got back w = %_" % [w])
 Compiling and running the program again prints out Create coroutine
 Resume with 42
 Came in a = 42
 Passing out Timon
 Got back x = Timon
 
 Resume with 43
 Came in b = 43
 Passing out and
 Got back y = and
 
 Resume with 44
 Came in c = 44
 Passing out Pumbaa
 Got back z = Pumbaa
 
 Resume with 45
 Came in d = 45
 Coroutine is done
 Got back w = Done
 Thus, suspendsends its argument out from the coroutine, and returns the value sent into the coroutine.resumesends its argument into the coroutine, and returns the value sent out from the coroutine. Closing Things OffFrom outside the coroutine body, we may also choose to close a coroutine when we're finished with it. Let's try closing our coroutine after getting back "Pumbaa". println("Create coroutine")val co = Coroutine<Int,String>(my-process)
 
 println("\nResume with 42")
 val x = resume(co, 42)
 println("Got back x = %_" % [x])
 
 println("\nResume with 43")
 val y = resume(co, 43)
 println("Got back y = %_" % [y])
 
 println("\nResume with 44")
 val z = resume(co, 44)
 println("Got back z = %_" % [z])
 
 close(co)
 
 println("\nResume with 45")
 val w = resume(co, 45)
 println("Got back w = %_" % [w])
 Compiling and running the above prints out Create coroutine
 Resume with 42
 Came in a = 42
 Passing out Timon
 Got back x = Timon
 
 Resume with 43
 Came in b = 43
 Passing out and
 Got back y = and
 
 Resume with 44
 Came in c = 44
 Passing out Pumbaa
 Got back z = Pumbaa
 
 Resume with 45
 FATAL ERROR: Cannot resume coroutine. Coroutine is already closed.
 at core/core.stanza:984.13
 at core/core.stanza:862.16
 at core/core.stanza:897.40
 at core/core.stanza:862.16
 at test.stanza:40.8
 Checking a Coroutine's StatusThere are two functions for checking on the status of a coroutine, active?andopen?. Calling active?on a coroutine will returntrueif the coroutine's body is currently running, andfalseotherwise. Only active coroutines can be suspended or broken from. Calling open?on a coroutine will returntrueif the coroutine's body is not currently running and open to be resumed. Only open coroutines can be resumed. Nested CoroutinesA coroutine may also launch more coroutines. Notice that unlike yield, the calls tosuspend,break, andresumeexplicitly requires, as its first argument, the target coroutine. Being able to explicitly designate the target of thesuspend,break, andresumeoperations are key to allowing nested coroutines to work properly. Consider the following code, where the coroutine, co1, launches a second coroutine,co2, within its body. Then withinco2's body, there aresuspendandbreakcalls on bothco1andco2. Pay attention to how this interacts. val co1 = Coroutine<False,Int> $ fn (co1, x0) :val co2 = Coroutine<False,False> $ fn (co2, y0) :
 for i in 0 to false do :
 suspend(co1, i)
 if i == 5 :
 println("Breaking from coroutine 2!")
 break(co2, false)
 resume(co2, false)
 -1
 
 while open?(co1) :
 println(resume(co1, false))
 Compiling and running the above prints out 01
 2
 3
 4
 5
 Breaking from coroutine 2!
 -1
 The two nested coroutines are quite confusing. To get a better sense of what's happening, let's rewrite the second coroutine using the special case labelconstruct. val co1 = Coroutine<False,Int> $ fn (co1, x0) :label<False> break :
 for i in 0 to false do :
 suspend(co1, i)
 if i == 5 :
 println("Breaking from coroutine 2!")
 break(false)
 -1
 
 while open?(co1) :
 println(resume(co1, false))
 Compiling and running the above prints out the same message as before. It is highly unlikely that you will feel the desire to directly launch new coroutines from within other coroutines, as we did here. But this example shows that they nest appropriately and generally do the right thing. Thus, for whatever abstractions you build on top of Stanza's targetable coroutine system, you can rest assured that they will recurse and compose correctly.  Example: Key ListenerThe following example demonstrates using coroutines to easily implement a key listener that translates individual key presses into events on strings. Let us define a KeyListenertype, and its fundamental operation. deftype KeyListenerdefmulti key-pressed (c:Char) -> False
 A KeyListenerobject listens to individual key presses from a keyboard and translates them into higher level events. Here is the definition of the constructor function for aKeyListener. defn KeyListener (entered: String -> False) -> KeyListener
 KeyListenertakes a callback function calledenteredthat acceptsStringobjects. OurKeyListenertranslates key presses into calls toenteredon space-separated words. It also supports deleting characters with the backspace key, entering of double-quoted strings, and escaped double-quotes within a double-quoted string. Here is specifically what it has to do.
 A KeyListenerhas an internal buffer for storing characters. When keys corresponding to letters are pressed, they are stored into the internal buffer.When the backspace key is pressed, the last character is deleted from the internal buffer.
Once a full word is completed (indicated by the spacebar being pressed), the enteredfunction should be called with the contents of the internal buffer.If the double quote key is pressed, then this indicates that a string is being started, and all subsequent characters until the next double quote should be stored in the internal buffer. Upon completion of the string, the enteredfunction should be called with the entire contents of the internal buffer.During entering of a string, if a backslash character followed by a double quote character is entered, then the double quote character is stored in the internal buffer, and entering of the string continues. If the backslash character is not followed by a double quote then both characters are ignored.
 Coroutine FrameworkHere is the basic framework that we will use to ease programming the KeyListener. defn KeyListener (entered: String -> False) -> KeyListener :val co = Coroutine<Char,False> $ fn (co, c0) :
 ;Retrieve the next character
 defn next-char () :
 suspend(co, false)
 
 ...
 
 new KeyListener :
 defmethod key-pressed (this, c:Char) :
 resume(co, c)
 We immediately create a coroutine that accepts characters and sends back dummy values of type False. Within the coroutine body, the helper functionnext-charrequests the next character by suspending the coroutine and returning the next character sent back into the coroutine. A newKeyListenerobject is returned that callsresumeon the coroutine whenever a key is pressed. Buffer Managing RoutinesThe following definitions within the coroutine body help us manage the KeyListener's internal buffer. ;Buffer commandsval buffer = Vector<Char>()
 defn pop-char () :
 if not empty?(buffer) :
 pop(buffer)
 defn add-char (c:Char) :
 add(buffer, c)
 defn empty-buffer () :
 entered(string-join(buffer))
 clear(buffer)
 The buffer is represented as a vector of characters. pop-charremoves the last character in the buffer if possible.add-charadds the given character to the end of the buffer.empty-buffercalls theenteredcallback with the contents of the buffer, and then clears the buffer. Dispatch ModeThe key press parser operates in a number of different modes. The default mode is the dispatch mode, which determines which mode to next enter based on the previously pressed key.  ;Dispatch modedefn* parse (c:Char) :
 if letter?(c) :
 parse-word(c)
 else if c == '\"' :
 parse-string(next-char())
 else :
 parse(next-char())
 If a letter key was pressed, then we start parsing a word event. If the double-quote key is pressed, then we start parsing a string event. Otherwise, the key press is ignored. Word ModeThe word parsing mode accepts key presses until one word is completed. ;Word parsing modedefn* parse-word (c:Char) :
 if letter?(c) :
 add-char(c)
 parse-word(next-char())
 else if c == '\b' :
 pop-char()
 parse-word(next-char())
 else if (c == ' ') or (c == '\"') :
 empty-buffer()
 parse(c)
 else :
 parse-word(next-char())
 If the last key pressed was a letter, then that letter is added to the buffer. If the last key was a backspace, then we delete a character from the buffer. If the last key was the spacebar or the double-quote key, then the word is completed. We empty the buffer and then go back to the dispatch mode. All other keys are ignored. String ModeThe string parsing mode accepts key presses until another (un-escaped) double-quote key finishes the string. ;String parsing modedefn* parse-string (c:Char) :
 if c == '\"' :
 empty-buffer()
 parse(next-char())
 else if c == '\b' :
 pop-char()
 parse-string(next-char())
 else if c == '\\' :
 if next-char() == '\"' : add-char('\"')
 parse-string(next-char())
 else :
 add-char(c)
 parse-string(next-char())
 If the last key pressed was the double-quote key then the string is completed. We empty the buffer and then go back to the dispatch mode. If the last key was a backspace, then we delete a character from the buffer. If the last key was the backslash key, then we add a double-quote to the buffer if the following key is a double-quote. Otherwise both keys are ignored. Finally, all other characters are added to the buffer. Testing the KeyListenerHere is the entire KeyListenerconstructor function. defn KeyListener (entered: String -> False) -> KeyListener :val co = Coroutine<Char,False> $ fn (co, c0) :
 ;Retrieve the next character
 defn next-char () :
 suspend(co, false)
 
 ;Buffer commands
 val buffer = Vector<Char>()
 defn pop-char () :
 if not empty?(buffer) :
 pop(buffer)
 defn add-char (c:Char) :
 add(buffer, c)
 defn empty-buffer () :
 entered(string-join(buffer))
 clear(buffer)
 
 ;Dispatch mode
 defn* parse (c:Char) :
 if letter?(c) :
 parse-word(c)
 else if c == '\"' :
 parse-string(next-char())
 else :
 parse(next-char())
 
 ;Word parsing mode
 defn* parse-word (c:Char) :
 if letter?(c) :
 add-char(c)
 parse-word(next-char())
 else if c == '\b' :
 pop-char()
 parse-word(next-char())
 else if (c == ' ') or (c == '\"') :
 empty-buffer()
 parse(c)
 else :
 parse-word(next-char())
 
 ;String parsing mode
 defn* parse-string (c:Char) :
 if c == '\"' :
 empty-buffer()
 parse(next-char())
 else if c == '\b' :
 pop-char()
 parse-string(next-char())
 else if c == '\\' :
 if next-char() == '\"' : add-char('\"')
 parse-string(next-char())
 else :
 add-char(c)
 parse-string(next-char())
 
 ;Launch!
 parse(c0)
 
 new KeyListener :
 defmethod key-pressed (this, c:Char) :
 resume(co, c)
 Let's try it out on some simulated key presses. defn keys-pressed (kl:KeyListener, cs:Seqable<Char>) :do(key-pressed{kl, _}, cs)
 
 defn main () :
 val kl = KeyListener $ fn (s) :
 println("String entered: %_" % [s])
 
 ;Test backspace
 keys-pressed(kl, "Timom\bn ")
 
 ;Test simple word
 keys-pressed(kl, "and ")
 
 ;Test backspace against empty buffer
 keys-pressed(kl, "P\b\b\b\bPumbaa")
 
 ;Test unrecognized characters
 keys-pressed(kl, " a#$!re")
 
 ;Test strings with escaped quotes
 keys-pressed(kl, \<S>"\"good\" friends!!"<S>)
 
 main()
 Note that  \<S>literal !@#$%\|" characters<S>
 is Stanza's syntax for a literal un-escaped string. All characters between the starting <S>tag and the ending<S>tag are part of the string. Any tag may be used in place ofS. Compiling and running the above prints out String entered: TimonString entered: and
 String entered: Pumbaa
 String entered: are
 String entered: "good" friends!!
 Our KeyListenercalls the callback function at the correct times and with the correct input! The coroutine mechanism allowed us to keep the code fairly straightforward and modular, even though the logic behind the KeyListeneritself is actually quite sophisticated. As an exercise, you may try to implement an equivalentKeyListenerfunction without using the coroutine mechanism to fully appreciate how tedious and error-prone it is. |