Patterns

Patterns refer more generally to the names that succeed as and let. Prowl implements both destructuring and pattern matching, as we will see.

Most of the time, patterns are simply an identifier. This binds the expression to the pattern, and keeps it in the context. Patterns concatenate, allowing us to bind multiple values. However, we can also use pattern literals to assert for certain values.

Matching

A pattern such as as 0 -> will assert that the value that as pops is equal to the integer literal 0. If true, it simply carries on the execution past -> without any substitution. If false, the program is rejected, meaning it fails, and the program tells you this.

0 as 0 -> 5

==> 5

1 as 0 -> 5

==> rejected

Eithers

Let's introduce our first nontrivial data type - eithers. Eithers create a union between two types. As there can only be two of them, values are tagged either left or right.

A left value can be constructed with (... ;)

(5;)

A right value can be constructed with (; ...)

(;5)

As before, you can assert that a value is left or right, and extract that value once you've confirmed which kind it is.

(;5) as (;x) -> x

==> 5

These structures are useful when working with a value that can be two different types, e.g. an int or a str, and will appear more motivated once we explore error handling.

Destructuring

Pairs

Pairs can be created using (..., ...). Pairs place two values in a single spot in the stack. (5, 4) would place the pair of 5 and 4 on the stack.

Pairs can be destructured, meaning their value can be extracted. In this way, they are similar to eithers, but the pairs pattern cannot fail on its own in the absense of other patterns or type errors.

(3, 4) as (x, y) -> x ** 2 + y ** 2

==> 25

Captures

Captures are a special and important kind of data. They allow functions, which normally pop and push to the stack, to be literals that are placed on the stack. Captures may be known as quotes in other concatenative languages. However, Prowl captures are opaque - they can only be used for concatenative logic, not for metaprogramming.

Captures are created using { ... }, as in Kitten. They thunk the expression inside of them, meaning they stop execution. The expression {3 4} places, on the stack, the function that pops none and pushes 3 and 4. The expression {+ 1} pushes to the stack the function that pops an integer, adds 1 to it, and pushes the new number.

While there are many ways to call captures, including concatenative combinators and many library functions, one way is to simply use pattern matching.

{7 8} as {f} -> f (*)

==> 56

The previous example pushes the function that places 7 and 8 to the stack. Then, the capture is destructured in as, and the bare function is bound to f. f is then executed, placing 7 and 8 on the stack, and then they are multiplied.