Private Types

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.

Motivation

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.

Basics

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.

Private Values

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.

Example

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 in the Language Specification

Private types are described in section 9 of the fble language specification.

Next Steps

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.