Leaf
write beautiful programs

Understanding Leaf Conditionals

Leaf presents a unified view of conditionals. At first this may look somewhat foreign, but we assure you that it's highly useful. Let's start with a basic if-else statement in pseudo-code.

1
2
3
4
if cond
    then-code
else
    else-code

In Leaf this is written as below.

1
2
3
4
5
do cond ? {
    then-code
} | {
    else-code
}

Squint a bit at first and you can ignore the syntax, seeing the basic if-else statement. Or you could just substitute the words then and else for the ? and | operators. That wouldn't be too interesting though, and doesn't show off what Leaf has to offer. Instead let's uncover what the syntax really means.

The ternary conditional operator

Many languages offer what is known as the ternary conditional operator. This allows the evaluation of an expression based on some conditional. It's usefulness lies with its ability to be done inline: not needing a flow branching construct.

1
2
3
4
5
6
7
8
//ternary operator
value = cond ? true-value : false-value

//without ternary operator
if cond
    value = true-value
else
    value = false-value

In Leaf this ternary operation looks like below.

1
value = cond ? true-value | false-value

The ? was chosen since it seems like a nice symbol to represent a condition. It can be roughly read as "then" in a sentence. The | was chosen since it is already used to mean "or" in many contexts, and that is essentially what is happening here.

Leaf gives these operators individual meaning by relating them to optional types. The optional modifier means the value may or may not be set. Given this, the ? evaluates to "unset" if the condition is false, and the provided value when the condition is true.

1
2
3
4
5
6
7
//set a to 4
var a : integer optional = true ? 4 
//b will be unset
var b : integer optional = false ? 5

//note that the type is implied in Leaf, so the following is also okay (it's just nice to show the types for example clarity)
var a = true ? 4

The | operator takes an optional type on the left-side, returning it's value if set, otherwise returning the value on the right.

1
2
3
4
5
6
7
var a = true ? 4
//x = 4, since a was set to 4
var x = a | 8

var b = false ? 5
//y = 9, since b was not set
var y = b | 9

The ternary conditional operator is really just two binary operators used in sequence. (Technical note: the ternary version still exists in Leaf, but for the most part you can ignore that and view it always as two binary operators.)

1
2
3
4
5
var b = cond ? one | two

//equivalent to 
var a = cond ? one
var b = a | two 

Optionals

Leaf embraces optional as a critical type modifier. The above syntax relies on it, as do many other constructs. You may be familiar with this concept already from another language, where a null pointer, or special False/None value indicate a value is not set. Being part of Leaf's innate type system gives optionals a nice degree of elegance and flexibility.

Chaining the | operator is a natural consequence of what it does. Given a series of optional values you can make one list of | evaluations to find one that might be set. In the example below we use it to find the most appropriate setting for a particular variable.

1
var cfg = local_cfg | user_cfg | global_cfg | 5

Optionals also provide a natural way for a function to maybe return a value: for example, a collection will return the value when searched, but return an unset optional when nothing is found.

1
var opt = map.get("name") | 12

No dereferencing is required to use optionals. An attempt to use an unset value results in a runtime error.

1
2
3
var a : optional integer = map.get("name")
//prints 'a' if set, raises unset-optional otherwise
print(a)

Given how easy it is to use optionals it is also nice to create them quickly. This is what the ? operator does. Any value and conditional can quickly be converted into an optional.

1
var enc = compress ? zip_compress

If-Else

For Leaf, we looked at typical if-else statements and thought they don't look much different than the above conditionals. We want to treat code as a high-order entity anyway, so it makes sense to think of conditional statements in terms of optionals. Let's return to the first example:

1
2
3
4
5
do cond ? {
    then-code
} | {
    else-code
}

The ? and | have no special meaning here. The keyword do is really a special function taking one-parameter: an optional block of code. Drop the do and the expression still makes sense. Instead of an expression evaluating to an integer value like above, our expression evaluates to a block of code. You could even deconstruct this into a series of statements.

1
2
3
4
5
6
7
8
then_code = {
    //then-code
}
else_code = {
    //else-code
}
var try_first = cond ? then_code
do try_first | else_code

(Sorry, you can't yet do this yet in Leaf, but the example still demonstrates the way you should understand the code. This will work in the future.)

Other constructs

Taking this approach provides a unified syntax for flow-like conditionals as well as expression-conditionals. It also allows an easy way to mix blocks of code, closures, and functions, without requiring additional syntax for each case. do is merely a flow operator which evaluates a potentially optional block of code.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
//code is not optional, thus always executed
do {
    //code
}

//code is optional (traditional "if")
do cond ? {
    //then-code
}

//code evaluates to one of two blocks (traditional "if-else")
do cond ? {
    //then-code
} | {
    //else-code
}

You can also substitute the keyword "do" with "loop" and get a looping construct instead. (If you spot the difficulty in deconstructing a loop, as we did with "do", congratulations! If you have a solution, please contact us!)

1
2
3
4
5
6
7
8
9
//an infinite loop as the code is not optional
loop {
    //code
}

//loop until condition is false (traditional while-loop)
loop cond ? {
    //loop-body
}

For even more flexibility a list-based conditional is provided. The select keyword evaluates to the first item in the list that has a value. That is, given a list of optionals, the first one which is set, is the result of the select statement. This is what it looks like in practice.

1
2
3
4
5
var r = select[
    cond_a ? result_a,
    cond_b ? result_b,
    otherwise
]

The list is merely a standard tuple. The first two entries use ? to create optionals (either set or unset). The final otherwise is not optional thus always the final callback. Again this can be combined with do keyword to produce a traditional switch.

1
2
3
4
5
6
7
8
9
do select[
    cond_a ? { 
        code_a 
    },  cond_b ? {
        code_b {
    },  {
        default_code
    }
]