PrefaceThe library is divided into 5 parts (in the user area) which serve complementary purposes.
| Preface.Specs |
Contains all the interfaces of the available abstractions.
The specifications resemble the _intf suffixed
signatures found in other libraries in the OCaml ecosystem.
|
| Preface.Make |
Contains the set of functors (in the ML sense of the term)
for concretising abstractions. Schematically, a module in
Preface.Make takes a module (or modules) respecting a
signature described in Preface.Specs to produce a
complete signature (also described in Preface.Specs).
|
| Preface.Laws | Functors to generate laws for a given abstraction. |
| Preface |
Contains concrete implementations, constructs that implement
abstractions described in Preface.Specs by means of
the functors present in Preface.Make.
This library is, at least, an example of the use of
Specs and Make.
|
Functor (in Haskell sense), Applicatives and monads are some of the best known abstractions in functional programming. Indeed, they allow recurrent problems to be solved in an elegant way. Generally, thanks to certain mechanisms linked to the languages that implement them (in Haskell, for example, using typeclasses), it is possible, by defining only a small subset of their combinators, to derive many others. So the purpose of "this part of the library" is to provide mechanisms for deriving combinators for a given type and a chosen abstraction, respecting OCaml programming idioms as much as possible.
This module describes the specifications of the abstractions provided by Preface. These specifications, which correspond to interfaces (module types in OCaml terminology) serve as constraints for the functors described in Preface.Make and centralise the documentation. Using a separate module allows cyclic dependencies to be resolved if one module can be described by another module and vice versa.
module Specs = Preface_specsIn order to produce embodiments for the abstractions described in Preface.Specs, Preface.Make offers a collection of functors that take modules constrained by the interfaces described in Preface.Specs to produce modules that respect the more complete interfaces also described in Preface.Specs.
module Make = Preface_makeThe modular design of Preface may seem a little intimidating at first glance. Let's look at the logic of the cut to understand how best to use it to describe new achievements of abstractions.
Abstractions must respect a minimum interface, however, sometimes there are several paths to describe the abstraction. For example, building a Monad on a type requires a return (or pure depending on the convention in practice) and:
bind/>>=map and join>=>In addition, on the basis of these minimum combinators, it is possible to derive other combinators. However, it happens that these combinators are not implemented in an optimal way (this is the cost of abstraction). In the OCaml ecosystem, the use of polymorphic variants is sometimes used to give the user the freedom to implement, or not, a function by wrapping the function definition in a value of this type:
val f : [< `Derived | `Custom of 'a -> 'b ]Instead of relying on this kind of (rather clever!) trick, we decided to rely mainly on the module language.
To make it easy to describe the embodiment of an abstraction, but still allow for the possibility of providing more efficient implementations (that propagate new implementations on aliases, such as infix operators, or functions that use these functions), Preface proposes a rather particular cut.
Each abstraction is broken down into several sub-modules:
Core
|
This module describes all the fundamental operations. For example,
for a monad, we would find return, map,
bind, join and
compose_left_to_right
|
Operation
|
The module contains the set of operations that can be described
using the Core functions.
|
Infix
|
The module contains infix operators built on top of the
Core and Operation.
|
Syntax
|
The module contains the let operators (such as
let* and let+ for example), built with
the Core and Operation functions.
|
Sometimes it happens that some modules are not present (e.g. when there are no infix operators) or sometimes some additional modules are added, but in general the documentation is clear enough.
The functors exposed in Preface.Make allow you to build each component one by one (Core, Operation, using Core, and Infix and Syntax using Core and Operation) and then group all these modules together to form the abstraction. Or use the Happy Path, which generally offers a similar approach to functors which builds Core but builds the whole abstraction.
Although it is likely that the use of the Happy Path covers a very large part of the use cases and that it is not necessary to achieve every abstraction by hand, it is still possible to do so.
In addition, it is sometimes possible to describe one abstraction by specialising another. In general, these specialisations follow this naming convention: From_name (More_general_module) or To_name (Less_general_module) and sometimes you can build a module on top of another, for example Selective on top of Applicative and the naming follows this convention: Over_name (Req), ie: Selective.Over_applicative.
Whereas the previous section dealt mainly with the achievements of abstractions (using functor machinery). This section documents the standard Preface library. A collection of already implemented abstractions for relatively common data structures.
module Void = Preface_stdlib.Voidmodule Identity = Preface_stdlib.Identitymodule Option = Preface_stdlib.Optionmodule Either = Preface_stdlib.Eithermodule Pair = Preface_stdlib.Pairmodule List = Preface_stdlib.Listmodule Nonempty_list = Preface_stdlib.Nonempty_listmodule Seq = Preface_stdlib.Seqmodule Stream = Preface_stdlib.Streammodule Exn = Preface_stdlib.Exnmodule Result = Preface_stdlib.Resultmodule Validation = Preface_stdlib.Validationmodule Try = Preface_stdlib.Trymodule Validate = Preface_stdlib.Validatemodule Fun = Preface_stdlib.Funmodule Predicate = Preface_stdlib.Predicatemodule Equivalence = Preface_stdlib.Equivalencemodule Continuation = Preface_stdlib.ContinuationThere are some (monad or comonad) transformers defined in Spec/Make. In Stdlib these are some concretised version using Identity as inner monad or comonad.
module Reader = Preface_stdlib.Readermodule Writer = Preface_stdlib.Writermodule State = Preface_stdlib.Statemodule Store = Preface_stdlib.Storemodule Env = Preface_stdlib.Envmodule Traced = Preface_stdlib.TracedApplicatives, Selectives, Profunctors and Arrows allow, contrary to monads, to perform static analyses on calculation workflows. Over and Under allow optimistic or pessimistic approximations.
module Approximation = Preface_stdlib.ApproximationMany of the abstractions presented in Preface are governed by laws, to ensure the proper functioning of other derived operations. This library provides functors to generate implemented laws for a concretization of an abstraction.
module Laws = Preface_lawsmodule Qcheck = Preface_qcheck