• Stars
    star
    148
  • Rank 249,983 (Top 5 %)
  • Language
    OCaml
  • License
    MIT License
  • Created over 5 years ago
  • Updated over 1 year ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Preface is an opinionated library designed to facilitate the handling of recurring functional programming idioms in OCaml.

Preface

Preface is an opinionated library designed to facilitate the handling of recurring functional programming idioms in OCaml. Many of the design decisions were made in an attempt to calibrate, as best as possible, to the OCaml language. Trying to get the most out of the module language. The name "preface" is a nod to "Prelude".

When learning functional programming, one is often confronted with constructs derived (or not) from category theory. Languages such as Haskell offer very complete libraries to use them, and thus, facilitate their learning. In OCaml, it often happens that these abstractions are buried in the heart of certain libraries/projects (Lwt, Cmdliner, Bonsai, Dune etc.). This is why one of the objectives of Preface is to propose tools for concretising these abstractions, at least as a pedagogical tool.

Is Preface useful

Since OCaml allows for efficient imperative programming, Preface is probably not really useful for building software. However, we (the maintainers) think that Preface can be useful for a few things:

  • technical experimentation with abstractions (especially those from the Haskell world) that allow programming in a fun style.
  • As an educational tool. Many teaching aids generally only offer the minimal interfaces to these abstractions. Preface tries to be as complete as possible.
  • It was a lot of fun to make. The last point is obviously the lightest but building Preface was really fun! So even if some people won't see the point... we had fun making it!

Installation

The package is available on OPAM, opam install preface should be enough. (And by describing, of course, OPAM in your project OPAM file and linking it to your project in the standard way proposed by your build-system.)

OPAM pin

If you would like to use the latest version of Preface (under development) you can use the pin mechanism.

...
depends: [
  ...
  "preface" {pinned}
]

pin-depends: [
  ["preface.dev" "git+ssh://[email protected]/xvw/preface.git"]
  ...
]
...

Esy resolution

The library can also be installed with esy using a resolution in your package.json file :

...
    "dependencies": {
      ...
      "@opam/preface":"*"
    },
    "resolutions": {
        "@opam/preface":"xvw/preface#<commit>"
    },
...

The pattern of the resolution is xvw/preface#<commit> where <commit> is mandatory and should point to a specific commit.

Library anatomy

The library is divided into four parts (in the user area) which serve complementary purposes.

Library Description
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.stdlib 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.
preface Packs all libraries making Preface.Specs and Preface.Make accessible as soon as Preface is available in the current scope. And includes Preface.Stdlib (so everything in Preface.Stdlib is available from Preface).

Available abstractions in Make and Specs

Although Stdlib offers common and, in our view, useful implementations, the heart of Preface lies in its ability to build the concretisation of abstractions for all sorts of data structures. Here is a list of abstractions that can be built relatively easily. As you can see, the diagram is heavily inspired by the Haskell community's Typeclassopedia.

typeclassopedia

Obviously, the set of useful abstractions is still far from being present in Preface. We have decided to privilege those for which we had a short and medium term use. But if you find that an abstraction is missing, the development of Preface is open, don't hesitate to contribute by adding what was missing.

Concretisation in Stdlib

As for the implemented abstractions, we favoured objects that we often manipulated (that we constantly reproduced in our projects) and also those that allowed us to test certain abstractions (Predicate and Contravariant for example). Don't hesitate to add some that would be useful for the greatest number of people!

Name Description Abstractions
Approximation.Over A generalization of Const (the phantom monoid) for over approximation Applicative, Selective
Approximation.Under Same of Over but for under approximation Applicative, Selective
Continuation A continuation that can't be delimited Functor (and Invariant), Applicative, Monad
Env The env comonad using Identityas inner monad Functor (and Invariant), Comonad
Either Represent a disjunction between left and right Bifunctor and can be specialised for the left part; Functor (and Invariant), Alt, Applicative, Monad, Traversable through Applicative and Monad, Foldable
Equivalence A generalization of function 'a -> 'a -> bool Contravariant (and Invariant), Divisible, Decidable
Fun Function 'a -> 'b Profunctor, Strong, Choice, Closed, Semigroupoid, Category, Arrow, Arrow_choice, Arrow_apply
Identity A trivial type constructor, type 'a t = 'a Functor (and Invariant), Applicative, Selective, Monad, Comonad
List The standard list of OCaml Foldable, Functor (and Invariant), Applicative, Alternative, Selective, Monad, Monad_plus, Traversable through Applicative or Monad, Monoid (where the inner type must be fixed)
Nonempty_list A list with, at least, one element Foldable, Functor (and Invariant), Alt, Applicative, Selective, Monad, Comonad, Traversable through Applicative or Monad, Semigroup (where the inner type must be fixed)
Option Deal with absence of values Foldable, Functor (and Invariant), Applicative, Alternative, Monad, Monad_plus, Traversable through Applicative of Monad, Monoid (where the inner type must be fixed)
Predicate A generalization of function 'a -> bool Contravariant (and Invariant), Divisible, Decidable
Reader The reader monad using Identity as inner monad Functor (and Invariant), Applicative, Monad
Result Deal with Ok or Error values Bifunctor and can be specialised for the error part; Functor (and Invariant), Alt, Applicative, Monad, Traversable through Applicative and Monad, Foldable
Seq The standard sequence of OCaml Foldable, Functor (and Invariant), Applicative, Alternative, Selective, Monad, Monad_plus, Traversable through Applicative or Monad, Monoid (where the inner type must be fixed)
State The state monad using Identity as inner monad Functor (and Invariant), Applicative, Monad
Store The store comonad using Identityas inner monad Functor (and Invariant), Comonad
Stream Infinite list Functor (and Invariant), Applicative, Monad, Comonad
Traced The traced comonad using Identityas inner monad Functor (and Invariant), Comonad
Try A biased version of Result with exception as the error part Functor (and Invariant), Alt, Applicative, Monad, Traversable through Applicative and Monad, Foldable
Pair A pair 'a * 'b Bifunctor
Validate A biased version of Validation with exception Nonempty_list as invalid part Functor (and Invariant), Alt, Applicative, Selective, Monad, Traversable through Applicative and Monad, Foldable
Validation Like Result but the invalid part is a Semigroup for accumulating errors Bifunctor and can be specialized on the invalid part: Functor (and Invariant), Alt, Applicative, Selective, Monad, Traversable through Applicative and Monad, Foldable
Writer The writer monad using Identity as inner monad Functor (and Invariant), Applicative, Monad

Stdlib convention

As it is possible to take several paths to realise an abstraction, we decided to describe each abstraction in a dedicated sub-module. For example Option.Functor or Option.Monad to let the user choose which combinators to use.

Do not shadow the standard library

Although it was tempting to extend the standard OCaml library with this technique:

module Preface : sig
  module List : sig
    include module type of List
    include module type of Preface_stdlib.List
  end
end

We have decided not to do this to ensure consistent documentation (not varying according to the version of OCaml one is using).

Some design choices

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
  • or possibly >=>

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:

Submodule Role
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 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.

Here is an example of the canonical flow of concretisation of an abstraction:

module hierarchy

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 concretise 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

Projects using Preface

Project name Description Links
YOCaml YOCaml is a static blog generator that essentially takes advantage of Preface's Freer, Result, Validation and Arrow. Github repository
Muhokama A simple forum built on top of Dream, Caqti, Omd, Preface, Cmdliner and other useful OCaml libraries Github repository

You use Preface for one of your projects and you want to be in this list? Don't hesitate to open a PR or fill an issue, we'd love to hear from you.

closing remarks

Preface is a fun project to develop and we have learned a lot from it. We hope you find it useful and/or enjoyable to use. We are open to any improvements and open to external contributions!

We received a lot of help during the development of Preface. Feel free to go to the CREDITS page to learn more.

More Repositories

1

qian

A minimalist file-explorer using Electron via Elm.
Elm
205
star
2

muhokama

A simple forum built on top of Dream, Caqti, Omd, Preface, Cmdliner and other useful OCaml libraries
OCaml
57
star
3

mizur

Mizur is a tool to simplify the handling of units, and units coercion/mapping. (it is an evolution of Abacus)
Elixir
35
star
4

ppx_measure

Type safe unit of measure in OCaml with ppx
OCaml
28
star
5

ppx_debugger

A small interactive debugger for OCaml (using PPX)
OCaml
28
star
6

kronos

Management of arithmetic operations on dates
Elixir
24
star
7

planet

A timetracker and a page generator
OCaml
23
star
8

coers

A small library for coercion to primitive Erlang types.
Erlang
23
star
9

jsoo_router

A small router to write easily single-page-app in Js_of_ocaml
OCaml
23
star
10

quasar

my own standard library for OCaml for Js ecosystem, because it's fun to reinvent the wheel
OCaml
22
star
11

abacus

Abacus is a tool to simplify the handling of units
Elixir
22
star
12

ppx_refutable_test

Provide a testing environnement (refutable) for OCaml using -ppx
OCaml
20
star
13

js_of_ocaml_bootstrapper

A super simple library (with use case) for Js_of_ocaml
OCaml
15
star
14

drumaderian

Drumaderian is a small framework for building VideoGame over Js_of_ocaml using Canvas. But... still in progress !
12
star
15

RPGMaker

Outils pour le logiciel RPG Maker
Ruby
11
star
16

scope

Scope is a small module that provides two macros to facilitate function overload and local import/aliases execution.
Elixir
11
star
17

ocamlectron

[WIP] A small framework to build "electron app" using OCaml via Js_of_ocaml
OCaml
11
star
18

jsoo_broadcastchannel

Binding in Js_of_ocaml for the BroadcastChannel API
OCaml
10
star
19

olmi

Provide lightweight interface for monads in OCaml
OCaml
10
star
20

piplet

Small page generator (Work in progress)
OCaml
9
star
21

wob

Way of burrito is a sample Gem for "Apero Ruby"
Ruby
8
star
22

capsule

Micro blogging for fun and profit
OCaml
8
star
23

mini_yocaml

A dead simple implementation of a subset of YOCaml for teaching purpose
OCaml
8
star
24

talks

Slides et documents de mes présentations
HTML
7
star
25

gba-rom-template

A simple template to write GBA ROM
Makefile
7
star
26

old_blog

personnal webpage :)
HTML
6
star
27

jsoo_storage

A wrapper in Js_of_ocaml for the WebStorage API
OCaml
6
star
28

yocaml2-empty-template

A dead simple skeleton for bootstrapping a blog using YOCaml 2
OCaml
6
star
29

typed-narration

Some implementation about static narration checker
OCaml
4
star
30

microparser

A small paring-combinator library for OCaml
OCaml
4
star
31

phragment

A protocol for decentralized conversations where everyone is owner of its content
TeX
4
star
32

deal

Elixir
4
star
33

simple-platformer

A simple plaformer game :v
Haxe
3
star
34

brainfocaml

A small brainfuck interpreter (teaching material)
OCaml
3
star
35

ateliers

Participations au projet http://atelier-prog.github.io/
OCaml
3
star
36

tetra-master-util

Miscellaneous tools for the Final Fantasy IX Tetra Master card game
OCaml
3
star
37

xvw.github.io

hosting for my own blog, the generator and the content live here : https://github.com/xvw/capsule
HTML
3
star
38

BOA

Basic Ocsigen Application
OCaml
2
star
39

mini-runner

A mini runner in JavaScript
JavaScript
2
star
40

kronos_ecto

An adaptater to use Kronos as a Custom type for Ecto
Elixir
2
star
41

ur-project

A tiny piece of code for build a Ur/Web project
OCaml
2
star
42

hhd-ghost

Happy Hacking Day 2015
OCaml
1
star
43

diarium

OCaml
1
star
44

blog

sources for my blog
OCaml
1
star
45

my-dmlenu

Replacement for DMenu using DMLenu
OCaml
1
star
46

unmoral.js

laul
1
star