Declaring Things with Parameters

As Kim recently pointed out, we have several inconsistent ways of
declaring “things with parameters”:

  1. Methods. For example:

    Methods are not first class. Still, a typical method request,

    looks a bit like a function application.

  1. Blocks, which are first-class values. For example:

    Blocks denote objects, with a single method named apply that takes as many arguments as the block has parameters, in this case, exactly one. Note that the self inside that above block denotes not the block object, but the object in which the block itself lives.

    This block would be used as follows:

  1. Class constructors, which are also first-class values.

    Class constructors denote objects too; these objects have a single method named new that takes as many arguments as the class constructor has parameters. You can see examples of the application of the resulting class in the bodies of the methods + and -. Note that the self inside the above class constructor denotes not the class object that it constructs, but the object that will be created when the method new is invoked on the class. Note that we may want, in addition to the above, for this form to also bind Complex to a type that looks like the body of the class constructor:

  1. Fields, which are really a shorthand for method declarations. For example, in the above object constructor, we have

    which can be seen as a shorthand for

    But, you say, constant reader methods don’t have parameters. Quite true. However, variable writer methods do, and

    can be seen as a shorthand for

    which does have a parameter.

Notational Differences

There are several dimensions of difference in the above four notations:

  1. nominal versus anonymous. Method and field declarations always construct named entities; block and class constructors always create anonymous entities, which are typically returned as answers or bound to names in a separate step;
  2. parameter placement – the nominal forms put the parameters with the name; the anonymous forms put them after an invisible lambda;
  3. the meaning of self; and
  4. the presence or absence of a keyword; blocks alone have no keyword.

Open Questions

The elephant underlying this post is the question: should we abolish one or more of these forms, or seek to unify them? At present Andrew proposes that the answer to both of these questions should be no. We introduced each one for a good reason; I believe that we need them all. And I don’t think that a uniform syntax will make programs more readable; Beta did that, and I don’t think that it works.

Still, I may be wrong, and I may change my mind. Or we may want to discuss this further. This post provides a place for that discussion.

Grace Workshops

We’ve always claimed that Grace’s design would be provisional, especially early on, and that we were after feedback on the design, both here on the blog, and at workshops or events we were attending.

Last weekend we held couple of workshops in Darmstadt and London. Participating were Jonathan Aldrich, Gavin Bierman, Sophia Drossopoulou, Susan Eisenbach, Erik Ernst, Dick Gabriel, Theo D’Hondt, Carsten Kolassa, Michael Kölling, Dirk Riehle, Lawrence Tratt, Andrew Black & James Noble. They were workshops, work was done, which will result in an updated spec soon. The largest changes we’re contemplating are:

  • Requiring curly braces for all blocks. Because braces delay evaluation, they are semantically as well as syntactically important. Requiring braces will make this clearer to novices. Code layout must still be consistent with bracketing, and layout will still be used to elide semicolons. This means that Grace code will look more like Scala and Java and less like Python or Haskell.
  • Operator precedence for arithmetic operators only (+,-,*,/). All other operators will have no precedence, working left-to-right. Operators without precedence will still require explicit parentheses.
  • A streamlined class declaration and initialisation protocol that will soon be a topic of a blog post.

plus a range of minor changes, generally simplifying Grace by choosing just one option where we had been considering allowing programmers’ choices. Especially for novices, every option adds complexity to a language and makes it harder to learn, so we’re trying to purge unnecessary options from the design.

Sequential Control Structures

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:

and while loops:

Grace’s “for” loop will follow the “foreach” style.

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:

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:

for while loops, and for if statements:

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:

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:

although using it is a little ugly as the “exit” becomes an explicit continuation block:

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:

and will be caught by a “catch” statement that syntactically parallels
the “match” statement, e.g.:

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).

The Case for Case

We will need a case statement in Grace to cover both the kind of simple cases covered by switch statements in C-family languages (though with less error prone syntax) as well as typecase statements that will provide the introspection on types and internal structure information as Java’s instanceof and Smalltalk’s understands: and isKindOf: . We could also consider a richer style of pattern matching that takes advantage of ML and Haskell-style destructuring pattern matching, similar to that used by Scala and Newspeak.

The simplest use of a case statement would be the following (lifted from a Scala example):

The keyword “otherwise” could be replaced by “else” to keep down the number of keywords, but it is arguably a good idea for it to be distinct from the conditional alternative to help (human) parsing (but could be argued out of it).

Putting “match” second follows Scala, and looks like we are sending the match message to Number (in fact, match could be defined in Object or whatever we are calling Top) with a parameter which is a lexical closure, or something like it. On the other hand, putting match first follows most other Grace statements.

Going a step further

An interesting issue is whether or not to go farther with pattern matching. This would have power similar to that in Scala, and approach Newspeak, but it would work somewhat differently because our constructors work differently from theirs.

The idea is that classes that we would like to use pattern matching with would require a special method that would extract the values to be used in a pattern match. For the example with Some<T>, we might add:

Then we could match against an Option type like this:

A list data type might have an extractor:

where head and tail are instance variables. A match could then look like:

The real question is, first, whether the benefits are worth the extra features here; and second, whether to stop there or head for a full Newspeak style generalised match protocol. One advantage of a full protocol is that other useful matches – notably regexp string matching – may be able to be incorporated into the same construct.

An alternative: pattern matching lambdas

Rather than introducing a special match statement with syntax to handle multiple cases, we have an alternative proposal where any block (with parameters) acts as a single pattern-match against those parameters. It is an error if the block is called with parameters that don’t match.

Then, a match exp case block case block “method” combines several pattern-matching lambda-blocks into a full match. For example:

This pattern-matching-lambda proposal was inspired by the realisation that, in Grace, every block (lambda expression) is potentially a type-match. This is because any lambda could be called from a dynamically-typed context, and so Grace will need to implement that type-match anyway. It turns out that Haskell, at least, also supports destructuring in lambda expressions.

Of course, both the lambda design and the multiple case syntax give Grace anonymous partial function definitions — that’s a good thing. The choice here is between a special syntatic construct for handling a series of cases that is used only within a “match” statement, or generalising lambda blocks to support matching against a single case and having a match construct that just glues a series of lambdas together. Pattern match lambdas are potentially more flexible, but it’s not clear which is easier to implement.

Questions

  • How powerful should matches be:
    • separate value match and type match statements?
    • single match statement that handles both value and type matches?
    • …also including destructuring matches?
    • …also including a full OO matching protocol allowing first-class patterns and user-level matching?
  • Do we stay with the Scala/Newspeak design – simple method definitions which can have a match statement inside it?
    • or should we support multiple cases and matching for method definitions?
    • or head out towards multimethods and predicate dispatch?
  • Which alternative – a match statement or pattern-matching-lambdas?

Some References to Scala and Newspeak