fble-0.5 (2025-07-13,fble-0.4-212-ga8f8ad0f)
This tutorial walks you through the basics of fble: structs, unions, and
functions. You'll implement a bitwise AND
operation on a 4 bit
bit-vector.
In your favorite text editor, start a new file called Basics.fble
.
We'll start by reusing some imports from the HelloWorld tutorial:
# Imports @ String@ = /Core/String%.String@; % Str = /Core/String%.Str; % Strs = /Core/String%.Strs;
Next we define types for a single bit and a 4 bit bit-vector:
@ Unit@ = *(); @ Bit@ = +(Unit@ 0, Unit@ 1); @ Bit4@ = *(Bit@ 3, Bit@ 2, Bit@ 1, Bit@ 0);
This code defines three types, named Unit@
, Bit@
, and Bit4@
. The
language requires type names to end with the @
character, to distinguish
them from normal variable names.
The @
at the beginning of each line says we are defining names of types.
The syntax *(...)
is used for struct types and +(...)
for union
types.
The Unit@
type defined here is a struct type without any fields. We call
it Unit@
because there's only one possible value for the type. It is a
building block for the Bit@
type. Unit@
is equivalent to the ()
type in Haskell, or the type of a zero-element tuple ()
in Python.
The Bit@
type is defined as a union of two Unit@
types, labeled
0
and 1
. There are two possible values for the Bit@
type, a
Unit@
value tagged with 0
and a Unit@
value tagged with 1
.
Union values implicitly keep track of their tag, unlike in C. You can ask a
union value what its tag is and access the value associated with the tag.
More on that later. There is nothing special about the names of the fields
0
and 1
that we have chosen here. We could just as well have named
them zero
and one
. The fble language has no notion of built in
numbers, which means you are free to use numbers as identifiers for variable
names or field names.
Bit4@
is a struct type with four fields, named 3
, 2
, 1
, and
0
. Each field has type Bit@
. This represents a bit vector of 4 bits.
We'll call bit 3 the most significant bit and put it on the left as is
conventional, but it doesn't really matter for the bitwise operations
defined in this tutorial.
Now that we have the Bit@
and Bit4@
types defined, we can declare
some variables of those types:
Unit@ Unit = Unit@(); Bit@ 0 = Bit@(0: Unit); Bit@ 1 = Bit@(1: Unit); Bit4@ X = Bit4@(0, 0, 1, 1); Bit4@ Y = Bit4@(1, 0, 1, 0);
This code declares 5 variables, named Unit
, 0
, 1
, X
, and
Y
. The variable declarations start with the type of the variable,
followed by the name of the variable, =
, and the value of the variable.
The syntax to create a struct value is the struct type followed by the list of values for each field of the struct, in order, in parenthesis. The syntax to create a union value is the union type followed by the tag, a colon, and the value to use for that tagged field all in parenthesis.
Union and struct values are immutable, lightweight, and passed by value. A
decent implementation of fble will implement a value of Bit4@
like X
or Y
easily in a single machine word.
Next we'll define a couple of functions that implement a bitwise AND
operation: A function And
that computes the AND
operation on
individual bits, and a function And4
to compute bitwise AND
on our 4
bit bit-vectors:
(Bit@, Bit@) { Bit@; } And = (Bit@ a, Bit@ b) { a.?(0: 0, 1: b); }; (Bit4@, Bit4@) { Bit4@; } And4 = (Bit4@ a, Bit4@ b) { Bit4@(And(a.3, b.3), And(a.2, b.2), And(a.1, b.1), And(a.0, b.0)); };
The functions And
and And4
are variables just like Unit
, 0
,
1
, X
, and Y
. To declare these variables, we start with the type,
followed by the name, =
, and the their value. In this case we have
function types and function values instead of struct or union types and
struct or union values.
The syntax for a function type is the list of argument types in parenthesis,
followed by the return type of the function in braces. The type for the
And
function is (Bit@, Bit@) { Bit@; }
, which means it is a function
that takes two arguments of type Bit@
and returns a Bit@
as a
result.
The syntax for a function value is a list of named arguments in parenthesis
followed by the body of the function. The implementation of the And
function uses names a
and b
for the arguments. The body of the
function consists of a single expression, which will be evaluated to compute
the result of the function.
In the case of the And
function, the result is computed using the
conditional expression a.?(0: 0, 1: b)
. Conditional expressions, also
known in fble as "union select" expressions, are similar to the conditional
operator, if statements, or case statements in C and other languages. The
syntax for a conditional expression is the union value to switch on,
followed by .?
, followed by an expression to evaluate for each possible
tag of the union value. At runtime, the tag associated with the union value
is used to select the expression to return. In this case, if a
is tagged
with 0
, the result 0
will be returned, and if a
is tagged with
1
, the result b
will be returned. That defines an AND
operation.
The And4
function is similarly defined. It returns a Bit4@
value
whose fields are the result of applying the And
function to the
corresponding fields of the arguments a
and b
. For example,
And(a.3, b.3)
calls the And
function with bit 3 of a
and bit 3
of b
, and uses the result for bit 3 of the result of the And4
function.
We can apply our And4
function to variables X
and Y
to get a
result Z
:
Bit4@ Z = And4(X, Y);
We'd like to write some functions to convert Bit@
and Bit4@
values
to strings so we can print them out. These functions are similar to the
functions we have already written:
(Bit@) { String@; } ShowBit = (Bit@ a) { a.?(0: Str|0, 1: Str|1); }; (Bit4@) { String@; } ShowBit4 = (Bit4@ a) { Strs[ShowBit(a.3), ShowBit(a.2), ShowBit(a.1), ShowBit(a.0)]; };
We'll explain in more detail about the list and string literals used here in future tutorials.
All that's left is to set our output string and main function. We'll have
the program print out the values of X
, Y
, and Z
:
String@ output = Strs[ ShowBit4(X), Str|' AND ', ShowBit4(Y), Str|' = ', ShowBit4(Z)]; /Core/Stdio/StringO%.Run(output);
We can now run our program to try it out:
fble-stdio -p core -I . -m /Basics%
If all goes well, you should see the values of X
, Y
, and Z
printed out:
0011 AND 1010 = 0010
Try implementing bitwise NOT
, OR
and XOR
functions.
Try defining a Bit8@
type and implementing a bitwise And8
operation
for it. You could reuse Bit4@
for the definition of Bit8@
or just
use Bit@
like how we defined Bit4@
.
Try implementing a 4-bit Add
function that can add two Bit4@
interpreted as twos-complement integers.
Head over to the Variables
tutorial to learn all
about variables.