-
Notifications
You must be signed in to change notification settings - Fork 11
Introduction to FreeSpec.Exec
The present document remains a draft and must be considered as such.
Coq notably provides a nice purely functional language with dependent type called Gallina to write specifications, and facilities (including a tactic language called Ltac) to reason about their correctness. Unfortunately, Gallina has long and infamously been known as “the language with no hello world.” Although Coq has a nice feature called program extraction to turn Gallina specifications into executable Ocaml programs, it has remained cumbersome to use. The result of this situation is the small amount of “Coq flagship projects” (eg. CompCert).
FreeSpec.Exec, an extensible plugin provided by the FreeSpec framework, is an
attempt to improve this situation by making it easy to execute Gallina
specifications with side effects with coqc only. There is no need to deal with
extraction-related burden anymore.
For instance, here is how you write a hello world program in Coq using FreeSpec.Exec.
Require Import FreeSpec.Program.
Require Import FreeSpec.Stdlib.Console.
Definition hello {ix} `{Use Console.i ix}
: Program ix unit :=
Console.echo "Hello, world!".
Exec hello.And executing this program is simply a matter of calling coqc.
$ coqc hello.v
Hello, world!FreeSpec.Exec remains in an early stage of development. While the core of the plugin already works as intended, too few useful interfaces are supported at the moment.
Fortunately, because FreeSpec.Exec is extensible, you can already start experimenting with it by defining your own interfaces.
FreeSpec.Exec is extensible, meaning it is easy for Coq developers to define
their interfaces, implement primitive semantics in dedicated OCaml plugins, and
enjoy coqc being able to execute programs written thanks to these primitives.
In FreeSpec, primitives are modeled with Coq terms, and in FreeSpec.Exec, an
effectful_semantic of a given primitive is an OCaml function which computes a
result given the list of arguments of a particular primitive.
In FreeSpec.Exec, effectful_semantic is defined in OCaml as follows:
type effectful_semantic =
Constr.constr list -> Constr.constr… where the Constr.constr type (as defined in the Coq source in the
kernel/constr.ml file) allows for describing (and manipulating) Coq
terms. Hopefully, you should not have to learn how Coq works under the hood and
Constr.constr in particular, as FreeSpec.Exec intends to provide facilities to
fill the gap between Coq and OCaml world.
| Coq | OCaml | to OCaml | from OCaml |
|---|---|---|---|
Z |
int |
int_of_coqz |
int_to_coqz |
bool |
bool |
bool_of_coqbool |
bool_to_coqbool |
ascii |
char |
char_of_coqascii |
char_to_coqascii |
string |
bytes |
bytes_of_coqascii |
bytes_to_coqstr |
string |
string |
string_to_coqstr |
|
unit |
coqtt |
Let’s have a look with a concrete example. FreeSpec comes with a simple package called FreeSpec.Stdlib.Console whose purpose is to provide simple primitives to read from the standard input and write to the standard output.
The interface in FreeSpec.Stdlib.Console is defined as follows:
Module Console.
Inductive i: Type -> Type :=
| Scan: i string
| Echo: string -> i unit.
End Console.Scan of type Console.i unit is intended to be a primitive to read one line
from the standard input, while Echo s of type Echo unit is inteded to be the
primitive to write the string s into the standard output.
In FreeSpec.Stdlib.Console, the effectful semantic of scan is implemented as follows:
let scan = function
| [] -> string_to_coqstr (read_line ())
| _ -> assert falseWe convert the result of read_line () (of OCaml type string) into a
Constr.constr value using string_to_coqstr. Because scan does not have any
parameter, we raise an error if a non-empty list is passed to us as input. In
such a case, this means there is a bug in FreeSpec.Exec, or that the Coq
definition of Scan as been changed.
The effectful_semantic of Echo is not harder to write:
let echo = function
| [str] -> print_bytes (bytes_of_coqstr str);
coqtt
| _ -> assert falseWe convert the Coq term passed as an argument of Echo using bytes_of_coqstr,
then we send it to the standard output using print_bytes. We then return a Coq
term (tt) whose Constr.constr representation in OCaml is coqtt.
Now that we have defined effectful semantics for the primitives of Console.i,
we have everything we need to emulate in OCaml the missing piece of Program
evaluation, that is a function of type:
forall {A: Type}, Console.i A -> AThe next and last step is to let FreeSpec.Exec know about these semantics (“to register our effectful_semantics”).
The Extends module provides a function to that end:
val register_interface :
(* The path of the module within the interface type
lives. *)
string list
(* A list to map each constructor of this interface
to an effectfull semantic. *)
-> (string * effectful_semantic) list
-> unitThe type signature of register_interface is pretty self-explanatory.
let _ =
register_interface
["FreeSpec"; "Stdlib"; "Console"; "Console"]
[("Scan", scan); ("Echo", echo)]FreeSpec.Exec provides a novel approach to turn Gallina into a real programming language. It currently lacks a comprehensive library of primitives, but it is extensible so that you do not have to wait for us to start testing it.