Planet JFX

This is a proposal (discussion document); it assumes the Tuples_and_Arrays proposal for specifics.

Patterns are used for variable declarations, function declarations, and "switch" expressions.

The syntax here is just illustrative.

First, let's generalize let-declarations to take a pattern:

 "let" Pattern "=" Expression

The Expression is evaluated, and the result matched against Pattern. The match may fail - which in this case throws an exception. If the match succeeds, zero or more names may be bound to values, usually "parts" extracted from the Expresion's value. Examples:

 let x = 10; // Succeeds and binds x to 10.
 let (x) = 10; // Same.
 let x : Number = 10; // Likewise.
 let x : Double = 10; // Binds x to the coerced value - i.e. 10.0.
 let x : Number = "10"; // Fails.
 let (x, y) = (2,3); // Binds x to 2 and y to 3.
 let (x) = (2,3); // Fails.
 let (x, y) = 2; // Fails.
 let (x, @y) = (2,3,4) // Bind x to 2 and y to [3,4].
 let (x, @y) = 2 // Bind x to 2 and y to [].
 let [x, y] = [2, 3];  // Binds x to 2 and y to 3.
 let [x, @y] = [2, 3];  // Binds x to 2 and y to [3].

For simplicity, we only allow "@X" at the end of a tuple. Here X is a pattern which is bound to a sequence (or map - details needed) containing the "rest" of the input tuple.

Formal parameter lists are patterns:

 function f(x, y) { "x is {x} and y is {y}" }
 f(2,3) ==> "x is 2 and y is 3"

The @ operation lets us do general "apply":

 { let x = [2,3]; f(@x) } ==> "x is 2 and y is 3"

Variable-length parameter list:

 function f(x, @y) { "x is {x} and y is {y}" }
 f(2) ==> "x is 2 and y is []"
 f(2, 3) ==> "x is 2 and y is [3]"
 f(2, 3, 4) ==> "x is 2 and y is [3, 4]"

A special pattern for "optional parameters" is useful:

 Pattern "default" DefaultExpression.

If there are more items in the input tuple, the Pattern is matched against the next item. Otherwise, the DefaultExpression is evaluated, and matched against the pattern. For example:

 function f(x, y default 0) { "x is {x} and y is {y}" }
 f(2, 3) ==> "x is 2 and y is 3"
 f(2) ==> "x is 2 and y is 0"
 f() ==> fails
 f(2, 3, 4) ==> fails

Keyword patterns provide support for named parameters. These are searched for and handled before other patterns, by ignoring the order, allowing keyword paramaters in any order.

 Name ":" [Pattern | ":" Type] ["default" DefaultExpression]

If *any* item in the argument map has a key matching "Name", then match the corresponding value against Pattern. Also, remove the corresponding map entry pair from the argument map before continuing. If there is no key matching "Name", evaluate DefaultExpression, and match that against Pattern. As a short-hand, if Pattern is missing, use either Name or (Name : Type), respectively.

More patterns for general maps can be useful, but we'll skip discussion here.

=case patterns and functions (switch)[]

Full benefit from patterns comes from "multi-rule" functions, where we have a sequence of patterns and actions, that are tried in order. Assume this syntax (for illustrative purposes):

 "{" Pattern1 "=>" Body1 [";" Pattern "=>" Body ]* "}"

For example:

 let add = { (x,y) => x+y };
 let add1 = { x => add(x,1) };

Same as:

 fun add(x, y) { x+y }
 fun add1(x) { add(x,1) }

Here a function that takes a variable-length argument list, and adds up all the arguments:

 let sum = { () => 0; (x, @rest) => x + sum(@rest) }

Here in contrast is a function that takes a single argument, which is an array, and add all the elements of the array:

 let sum = { [] => 0; ([x, @rest]) => x + sum(rest) }

The SwitchExpression:

 "switch" ValueExpression FunctionExpression

is syntactic sugar for:

 FunctionExpression "(" ValueExpression ")"

For example:

 let x = 2;
 let y =
   switch (x) {
     1 => "one";
     2 => "two";
     _ => "many"
  };
 y ==> "two"

This add two new kinds of Pattern: A literal value (number or string) succeeds if the matched value is equal to the literal. The pattern _ matches any item, but doesn't declare any variable. This can be useful for a "default" case.

Languages with these kinds of patterns usually also support "constructor functions". For example:

 let Complex(x, y) = z;

which is equivalent to:

 let dummy : Complex = z;
 let x = z.re;
 let y = z.im;

But this only works for "simple" constructor functions, so let's leave this feature out, at least for now.

Back to OpenJFX Compiler