fble-0.5 (2025-07-13,fble-0.4-212-ga8f8ad0f)
This tutorial does a deep dive into private types in fble. By the end of this tutorial, you'll be fully versed in section 9 of the fble language spec on private types.
The way types work in fble, anyone can describe any type. You don't need to have access to a type declaration to refer to a type. One of the downsides of this approach to types is it makes it harder to protect internal data structures.
For example, imagine you want to define an auto-balancing tree structure for
use as a map data type. The internal structure of the tree may be
complicated and rely on invariants about how the tree is balanced. As long
as users only interact with the tree through provided functions like
Empty
, Lookup
, Insert
, and Delete
, those invariants can be
maintained. But technically there is nothing stopping a user from
constructing their own tree value directly and in a way that violates the
expected invariants.
Private types are a way to restrict access to types to a set of modules.
This way, for example, you can say only the /Core/Map%
module is allowed
to construct a map directly; everyone else has to use the functions provided
by /Core/Map%
to construct maps.
A private type is a guarded wrapper around a normal type that can only be accessed by modules in a specified subtree of the module hierarchy. For modules granted access to the type, the private type behaves exactly the same as the type it is wrapping. For modules not granted access to the type, the private type behaves like an abstract type.
For example, the normal type Bool@
is accessible to everyone. Anyone can
test if a boolean value is true or false or construct their own boolean
value using Bool@(true: Unit)
and Bool@(false: Unit)
.
We can define a private type MyBool@
that wraps the Bool@
type,
and make it so only modules in the /Mine/
part of the module hierarchy
can teset if the boolean value is true or false or construct their own.
@ MyPackage@ = @/Mine%; @ MyBool@ = Bool@.%(MyPackage@);
The syntax @
followed by a module path is used to describe a package
type. Package types represent sets of modules. In this case, because it is
given /Mine%
as an argument to the package type, it includes modules
like /Mine%
, /Mine/A%
, /Mine/A/B%
, and /Mine/C%
, but
excludes modules like /Yours%
.
Once you have the package type MyPackage@
, you can create
package-specific wrappers around other types. In this case, the type
MyBool@
is created as a wrapper around Bool@
.
To create a value of MyBool@
, you could use a constructor directly:
MyBool@ MyTrue = MyBool@(true: Unit);
Or you could assign a value directly from the Bool@
type:
MyBool@ MyTrue = Bool@(true: Unit);
The types MyBool@
and Bool@
are one and the same from the point of
view of modules with access to MyBool@
, they can be used
interchangeably.
From the point of view of modules that aren't in the set described by the
MyPackage@
type, MyBool@
is an opaque abstract type, in no apparent
way related to Bool@
. Attempts to construct values of MyBool@
or
assign from a Bool@
will be met with a type error.
The same syntax used to define a private type can be used to cast a value
into a private type. For example, assuming True
is of type Bool@
,
True.%(MyPackage@)
has the same value but is of type MyBool@
.
This is useful for creating implicit type struct values and function values of private types.
You can use private values to export data or functions from a module that can only be used by modules belonging to the specified package.
The typical approach for using private types is simply to mark your type of interest as private. Everything else is unchanged.
For example, here is how you could define a three element enum type that can only be accessed externally using the provided methods:
@ Enum@ = +(Unit@ a, Unit@ b, Unit@ c).%(@/MyPackage%); Enum@ A = Enum@(a: Unit)); Enum@ B = Enum@(b: Unit)); Enum@ C = Enum@(c: Unit)); (Enum@) { Bool@; } IsA = (Enum@ e) { e.?(a: True, : False); }; (Enum@) { Bool@; } IsB = (Enum@ e) { e.?(b: True, : False); }; (Enum@) { Bool@; } IsC = (Enum@ e) { e.?(c: True, : False); }; @(Enum@, A, B, C, IsA, IsB, IsC);
Private types are described in section 9
of the fble language specification
.
You've now learned about all of the fble language features. Continue on to
the Core Library
tutorial to learn about some common types
and functions defined in the core library.