Bindlib 6.0.0

Breaking changes:

Other changes:

This releases breaks your printing code: here is how to fix it

Note: only the printing code is broken, all the rest is fine.

To understand the problem, recall that variable substitutions does not perform any bound variable renaming in Bindlib. What used to be the case, was that the bound variable names were appropriately updated upon term construction. A term that was freshly constructed was hence guaranteed to be safe for printing, and thus it was advised to do a "cleanup" term traversal before printing (this was done by composing a user-defined boxing function of type t -> t Bindlib.box, and the build-in Bindlib.unbox function).

For example, with a type of the terms of the λ-calculus, that would be done as follows, using the appropriate smart constructors.

type term =
  | Var of term Bindlib.var
  | Abs of (term, term) Bindlib.binder
  | App of term * term

let var = Bindlib.box_var
let abs = Bindlib.box_apply (fun b -> Abs(b))
let app = Bindlib.box_apply2 (fun t u -> App(t,u))

let rec box_term: term -> term Bindlib.box = fun t ->
  match t with
  | Var(x)   -> var x
  | Abs(b)   -> abs (Bindlib.box_binder box_term b)
  | App(t,u) -> app (box_term t) (box_term u)

(* DOES NOT GUARANTEE THAT THE OUTPUT TERM IS SAFE FOR PRINTING ANYMORE. *)
let cleanup: term -> term = fun t ->
  Bindlib.unbox (box_term t)

(* Naive printing function assuming correct names. *)
let rec to_string: term -> string = fun t ->
  match t with
  | Var(x)   -> Bindlib.name_of x
  | Abs(b)   -> let (x,t) = Bindlib.unbind b in
                "λ" ^ Bindlib.name_of x ^ "." ^ to_string t
  | App(t,u) -> "(" ^ to_string t ^ ") " ^ to_string u

(* BROKEN PRINTING FUNCTION AS OF BINDLIB VERSION 6.0.0. *)
let to_string t = to_string (cleanup t)

What do we do now?

In this new version of Bindlib, a function that prints or displays a term must rely on a context (of type Bindlib.ctxt) for free variables. All renaming is performed when introducing fresh variables to substitute binders using special functions like Bindlib.new_var_in or Bindlib.unbind_in, which take care of picking a fresh name and maintaining the context.

Going back to the λ-calculus, the printing function must become the following.

(** [to_string_in ctxt t] is safe for printing assuming that [ctxt] is a valid
    context for [t] (i.e., it contains all of its free variables). *)
let rec to_string_in : ctxt -> term -> string = fun ctxt t ->
  match t with
  | Var(x)   -> Bindlib.name_of x
  | Abs(b)   -> let (x,t,ctxt) = Bindlib.unbind_in ctxt b in
                "λ" ^ Bindlib.name_of x ^ "." ^ to_string_in ctxt t
  | App(t,u) -> "(" ^ to_string_in ctxt t ^ ") " ^ to_string_in ctxt u

(** [to_string t] is always safe for printing. *)
let to_string : term -> string = fun t ->
  to_string_in (Bindlib.free_vars (box_term t)) t

In some situations where a form of context is required anyway (e.g., for doing type-checking), it might make sense to maintain a Bindlib context to avoid the cost of a call to box_term at printing time. However, this also means that a small cost for maintaining names is payed early on.

Bindlib 5.0.1 (21/08/2018)

Bug fixes:

Other changes:

Bindlib 5.0.0 (27/05/2018)

Breaking changes:

Other changes:

Bindlib 4.0.5 (24/01/2018)

Breaking changes:

Other changes:

Bindlib 4.0.4 (21/08/2017)

Breaking changes:

Other changes:

Bindlib 4.0.3

Breaking changes:

Other changes:

Bindlib 4.0.2

Breaking changes:

Other changes:

Bindlib 4.0

Major cleanup/rewriting of the library by Rodolphe Lepigre. First version distributed via Opam.

Earlier versions

All earlier versions of Bindlib were developed by Christophe Raffalli. A version for Haskell was even available at some point.