Bindings

In order to write more complicated programs, we need to have ways to manipulate the stack. One way to do this is with concatenative combinators - and Prowl has those - but they can sometimes be difficult to reason about, forming "stack chatter". Prowl uses lexically-scoped bindings as a more gentle and flexible means of stack manipulation, while leaving concatenative combinators for terseness where it is a boon.

As Expressions

as expressions pop the top element from the stack at the time of execution and store it in the following name.

5 as x -> x + 1

In this program, 5 is placed on the stack. Then, 5 is popped from the stack and stored in x. Following the right of the arrow, x places it's bound value 5 to the stack, and it is added to 1, producing 6.

x can also be used multiple times:

5 as x -> x * x

==> 25

as can pop and bind multiple values off the stack

3 4 as x y -> x ** 2 + y ** 2

=> 25

In general, the -> arrow symbolizes producing a new scope. You can think of these expressions performing a substiution and producing a new expression, e.g. (5 as x -> x * x) produces the new expression (5 * 5), then simplifies.

Let Expressions

let expressions bind the result of the value on the right-hand side of the assignment to the name on the left-hand side.

let x = 5 -> x * x

==> 25, as before

Let Functions

let expressions support functions in their right-hand side - expressions are able to pop values off of a stack that they can't see yet. This produces a binding that then pops values, which can act as a function.

/* names may use - */
let norm-sqr = as x y -> x ** 2 + y ** 2 ->
3 4 norm-sqr

==> 25

Unlike as expressions, having multiple names following let uses those names as arguments, as sugar for a following as expression.

let norm-sqr x y = x ** 2 + y ** 2 -> 
3 4 norm-sqr

==> 25

Concatenation

In the previous example, we can see that 3 4 norm-sqr produces 25 as norm-sqr pops the two values before it from the stack and then pushes the result. These are always evaluated in left-to-right Reverse Polish Notation (RPN) order. Juxtaposing functions next to each other composes them, and is called concatenation. Concatenation has higher precedence than all infix operators (barring module access and the rare infix regex operators).

Sectioning

Prowl implements Haskell-style sectioning. This turns the arithmetic operations into concatenative RPN ones.

4 5 (+)

==> 9

Wrapping an infix operator in parenthesis makes it behave as it does in many other stack languages: it pops its two args from the stack, performs its operation, and pushes its result to the stack.

Operator precedence no longer exists in this style.

2 1 (+) 2 (-) 4 (*)

==> 4

Operators can also be partially applied with a particular input in an infix-like fashion, just like in Haskell.

5 (- 1)

==> 4

5 (1 -)

==> -4

(a `op`) becomes x as a `op` x
(`op` b) becomes x as x `op` b

We can use this with let as we saw with as

let z = 0 -> 
let s = (+ 1) -> 
z s s s

==> 3