fble-0.5 (2025-07-13,fble-0.4-212-ga8f8ad0f)
This tutorial describes special syntax fble has for describing lists of values.
There is no builtin list type in fble. You can define your own list type in
fble using a mix of structs and unions. For example, here's a possible
definition for a list of Int@
:
@ List@ = +(*(Int@ head, List@ tail) cons, Unit@ nil);
This defines the list type as a union with two choices: a list constructed with a head element and tail of the list, or an empty list. We can define helper functions for constructing lists:
List@ Empty = List@(nil: Unit); (Int@, List@) { List@; } Cons = (Int@ head, List@ tail) { List@(cons: @(head, tail)); };
We can then construct a list of integers. For example, here's a list of the first 8 integers in the fibonacci sequence:
List@ Fibonacci = Cons( Int|0, Cons( Int|1, Cons( Int|1, Cons( Int|2, Cons( Int|3, Cons( Int|5, Cons( Int|8, Cons( Int|13, Empty))))))));
It's pretty tedious to write a list this way. In most languages you could
write something like [0, 1, 1, 2, 3, 5, 8, 13]
, which is much more
compact and readable.
Fble has syntax for list expressions. A list expression is a special kind of function application for functions that take a single list argument.
Let's define a (in this case trivial) function that takes a single list as an argument to construct a list:
(List@) { List@; } List = (List@ l) { l; };
We can use the list expression in fble to define lists now using:
List@ Fibonacci = List[Int|0, Int|1, Int|1, Int|2, Int|3, Int|5, Int|8, Int|13];
The compiler will assemble together the list elements into a collection of struct and union values that comprise the value of the list, and then call the function with the list value as its argument. In this case, the list expression desugars at compile time to:
List@ Fibonacci = List( List@(cons: @(head: Int|0, tail: List@(cons: @(head: Int|1, tail: List@(cons: @(head: Int|1, tail: List@(cons: @(head: Int|2, tail: List@(cons: @(head: Int|3, tail: List@(cons: @(head: Int|5, tail: List@(cons: @(head: Int|8, tail: List@(cons: @(head: Int|13, tail: List@(nil: Unit))))))))))))))))));
For the compiler to support the list expression, it has to know what the
structure of a list is. It doesn't care what name you use for the list type,
or what name you use for the list fields. As long as it has the same
structure as the List@
type we already defined.
Specifically, the required structure of a list type is:
If all of these are satisfied for your type, you can use a list expression to easily construct values of your type.
For example, here's a completely different way we could define our list type and still use the list expression:
@ MyUnit@ = *(); @ NonEmptyIntList@ = *(Int@ element, IntList@ rest_of_list), @ IntList@ = +(NonEmptyList@ not_empty, MyUnit@ empty); (IntList@) { IntList@; } Elements = (IntList@ elements) { elements; }; IntList@ Fibonacci = Elements[Int|0, Int|1, Int|1, Int|2, Int|3, Int|5, Int|8, Int|13];
This works because the IntList@
type, even though it uses different
names for things, matches the structure of a list type expected by the
compiler for list expressions.
We haven't covered polymorphism yet in these tutorials, but importantly, you can define polymorphic list types that satisfy the list type structure required by the list expression.
The compiler knows what actual type to use for the constructed list based on the argument type of the list function used in the list expression.
The list expression requires a function that takes a single argument which
is of list type. We saw an example function List
that returns its value
directly to construct a list, but you can do anything you want in the list
function.
For example, here is a list function that sums the integers in a list:
(List@) { Int@; } Sum = (List@ xs) { xs.?(nil: Int|0); Add(xs.cons.head, Sum(xs.cons.tail)); };
We can use Sum
as the list function in a list expression:
Int@ sum = Sum[Int|0, Int|1, Int|1, Int|2, Int|3, Int|5, Int|8, Int|13];
We saw some examples of the list expression in our
HelloWorld
tutorial. For instance:
String@ output = Strs[Str|'A boolean: ', /Core/Bool/Show%.Show(True)];
Here the Strs
function takes a list of strings as its only argument, so
we can use the list expression to call it.
The list expression can be used to provide something similar to variable arguments calls like they have in C, C++, and Java. If you want a function to take a variable number of arguments (which in fble must all have the same type), pass a list of values as one of the arguments and use a list expression to construct that list.
The list expression is covered in section 7.1
of the fble language specification
.
Head over to the Literals
tutorial to learn all about
literals in fble.