Control structures are important for imperative programming languages, and crucial for educational languages. The most important topics in a first imperative programming course are often sequence, selection, anditeration. Grace needs to support this pedagogy.
Grace’s control structures also need to help students to move onwards to other languages – particularly “curly brace” languages (C, C++, Java, etc) because, for most students, the majority of software they will have to deal with will be written in these languages. This means that Grace must facilitate reading and interpreting classically imperative syntax. On the other hand, Grace doesn’t need to replicate every nook and cranny of these languages. As C++ abstracted and simplified C, and Java abstracted and simplified C++, Grace can abstract and simplify Java (and Scala and Python and other languages too).
Selection and Iteration
So, as well as a pattern matching construct, Grace will support “if” statements:
123 if (test) then {block}if (test) then {block} else {block}
and while loops:
1 while {test} do {block}
Grace’s “for” loop will follow the “foreach” style.
123 for (collection) do {block}for (course.students) do { each => each.println() }
The difference here is that Grace’s blocks are true lambdas: rather than the for statement introducing a variable binding that is used “later on” — we declare the iteration variable at the start of the block. The key principles here are economy – variables are only introduced as parameter (or variable) declarations. As with any other formal argument in Grace, the block parameters are bound in a (conceptually) new instance of the block execution context for each iteration – so Grace is not suceptible to the kinds of problems in C# (that defines the foreach loop as repeatedly assigning to the same mutable variable).
As in Python, for can be used with a range to iterate through integers:
1 for (0..N) do { i | println(i); }
Control Structures Defined in Libraries
Grace’s syntax for control structures is precisely Grace’s syntax for message send — indeed, the Smalltalk-like multiple keyword messages and the layout syntax should work together to that end. The key reasons for this are that we want Grace to support different language levels, like Racket, and different pedagogies, particularly regarding concurrency. Instructors need to be able to define “language extensions” in libraries, ideally without needing any overly specialised constructors. Having a single general syntax also shouldmean Grace is easier to parse, and eventually to compile.
Using Grace’s keyword message syntax for control structures also means that the control structures can (at least conceptually) be defined in Grace, for example:
12345678 method while (test : Block<Boolean>) do (block : Block) : Void {if (test.apply()) thenblock.apply()elsereturnreturn (while (test) do (block))}
for while loops, and for if statements:
123 method <T> if (test : Boolean) then (trueBlock : Block<T>) : Void {return test.ifTrue(trueBlock)}
We don’t want to commit all Grace implementations to implement control flow structures in this way — Grace implementations may forbid redefinition of control flow messages, intercept call to them, and implement them directly. But (for more advanced students) we think being able to explain even basic control structures within Grace is an advantage.
Loop with exit
In an earlier design, we proposed a single “loop-with-exit” construct:
123456 var i = 0loop {if (i > 10) then {exit}println(i)i := i + 1}
James is currently leaning against this design because it’t not clear what all the semantics are – what is the “exit”, what is its scope, how it is resolved. As Andrew says, in Smalltalk, programmers just refactor and use “return” – and that will work in Grace.
Advanced programmers should be able to define loop-with-exit in Grace:
1234 method loop (block : Block<Void, Exit>) {block.apply({return});return loop(block);}
although using it is a little ugly as the “exit” becomes an explicit continuation block:
123456 var i = 0loop { exit =>if (i > 10) then {exit.apply()}println(i)i := i + 1}
This does indicate a weakness of Grace’s approach to defining syntax or language extensions: some things will be uglier than they could be if Grace had truly extensible syntax, e.g. like Lisp/Scheme/Racket macros. In this Grace also follows Smalltalk. We hope the advantages of a smaller language with more regular syntax will overcome the contortions that may occasionaly be required.
Exceptions
Grace needs an exception mechanism since exceptions are another common flow control construct of many programming languages, and students using Grace will need to be taught to use exceptions. We plan to support basic unchecked exceptions on the lines of ML, Java, C# etc.
Exceptions will be generated by the “raise” keyword (syntatically another message call) that takes an argument of some subtype of Exception:
1 raise new UserException("Oops...!")
and will be caught by a “catch” statement that syntactically parallels
the “match” statement, e.g.:
1234 catch {File.open("data.store")}case {e : NoSuchFile => println("No Such File"); return}case {e : PermissionError => println("No Such File"); return}case {Exception => println("Unidentified Error); System.exit()}
Exceptions can’t be restarted, however, the stack frames that are terminated when an exception is raised should be pickled so that they can be used in the error reporting machinery (debugger, stack trace).