212 lines
7 KiB
Org Mode
212 lines
7 KiB
Org Mode
* Base
|
|
|
|
[[https://github.com/janestreet/base/actions][https://github.com/janestreet/base/actions/workflows/workflow.yml/badge.svg]]
|
|
|
|
Base is a standard library for OCaml. It provides a standard set of
|
|
general purpose modules that are well-tested, performant, and
|
|
fully-portable across any environment that can run OCaml code. Unlike
|
|
other standard library projects, Base is meant to be used as a
|
|
wholesale replacement of the standard library distributed with the
|
|
OCaml compiler. In particular it makes different choices and doesn't
|
|
re-export features that are not fully portable such as I/O, which are
|
|
left to other libraries.
|
|
|
|
You also might want to browse the [[https://ocaml.janestreet.com/ocaml-core/latest/doc/base/index.html][API Documentation]].
|
|
|
|
** Installation
|
|
|
|
Install Base via [[https://opam.ocaml.org][OPAM]]:
|
|
|
|
#+begin_src
|
|
$ opam install base
|
|
#+end_src
|
|
|
|
Base has no runtime dependencies and is fast to build. Its sole build
|
|
dependencies are [[https://github.com/ocaml/dune][dune]], which itself requires nothing more than the
|
|
compiler, and [[https://github.com/janestreet/sexplib0][sexplib0]].
|
|
|
|
** Using the OCaml standard library with Base
|
|
|
|
Base is intended as a full stdlib replacement. As a result, after an
|
|
=open Base=, all the modules, values, types, ... coming from the OCaml
|
|
standard library that one normally gets in the default environment are
|
|
deprecated.
|
|
|
|
In order to access these values, one must use the =Stdlib= library,
|
|
which re-exports them all through the toplevel name =Stdlib=:
|
|
=Stdlib.String=, =Stdlib.print_string=, ...
|
|
|
|
** Differences between Base and the OCaml standard library
|
|
|
|
Programmers who are used to the OCaml standard library should read
|
|
through this section to understand major differences between the two
|
|
libraries that one should be aware of when switching to Base.
|
|
|
|
*** Comparison operators
|
|
|
|
The comparison operators exposed by the OCaml standard library are
|
|
polymorphic:
|
|
|
|
#+begin_src ocaml
|
|
val compare : 'a -> 'a -> int
|
|
val ( <= ) : 'a -> 'a -> bool
|
|
...
|
|
#+end_src
|
|
|
|
What they implement is structural comparison of the runtime
|
|
representation of values. Since these are often error-prone,
|
|
i.e. they don't correspond to what the user expects, they are not
|
|
exposed directly by Base.
|
|
|
|
To use polymorphic comparison with Base, one should use the =Poly=
|
|
module. The default comparison operators exposed by Base are the
|
|
integer ones, just like the default arithmetic operators are the
|
|
integer ones.
|
|
|
|
The recommended way to compare arbitrary complex data structures is to
|
|
use the specific =compare= functions. For instance:
|
|
|
|
#+begin_src ocaml
|
|
List.compare String.compare x y
|
|
#+end_src
|
|
|
|
The [[https://github.com/janestreet/ppx_compare][ppx_compare]] rewriter
|
|
offers an alternative way to write this:
|
|
|
|
#+begin_src ocaml
|
|
[%compare: string list] x y
|
|
#+end_src
|
|
|
|
** Base and ppx code generators
|
|
|
|
Base uses a few ppx code generators to implement:
|
|
|
|
- Reliable and customizable comparison of OCaml values.
|
|
- Reliable and customizable hash of OCaml values.
|
|
- Conversions between OCaml values and s-expression.
|
|
|
|
However, it doesn't need these code generators to build. What it does
|
|
instead is use ppx as a code verification tool during development. It
|
|
works in a very similar fashion to
|
|
[[https://github.com/janestreet/ppx_expect][expectation tests]].
|
|
|
|
Whenever you see this in the code source:
|
|
|
|
#+begin_src ocaml
|
|
type t = ... [@@deriving_inline sexp_of]
|
|
let sexp_of_t = ...
|
|
[@@@end]
|
|
#+end_src
|
|
|
|
the code between the =[@@deriving_inline]= and the =[@@@end]= is
|
|
generated code. The generated code is currently quite big and hard to
|
|
read, however we are working on making it look like human-written
|
|
code.
|
|
|
|
You can put the following elisp code in your =~/.emacs= file to hide
|
|
these blocks:
|
|
|
|
#+begin_src scheme
|
|
(defun deriving-inline-forward-sexp (&optional arg)
|
|
(search-forward-regexp "\\[@@@end\\]") nil nil arg)
|
|
|
|
(defun setup-hide-deriving-inline ()
|
|
(inline)
|
|
(hs-minor-mode t)
|
|
(let ((hs-hide-comments-when-hiding-all nil))
|
|
(hs-hide-all)))
|
|
|
|
(require 'hideshow)
|
|
(add-to-list 'hs-special-modes-alist
|
|
'(tuareg-mode "\\[@@deriving_inline[^]]*\\]" "\\[@@@end\\]" nil
|
|
deriving-inline-forward-sexp nil))
|
|
(add-hook 'tuareg-mode-hook 'setup-hide-deriving-inline)
|
|
#+end_src
|
|
|
|
Things are not yet setup in the git repository to make it convenient
|
|
to change types and update the generated code, but they will be setup
|
|
soon.
|
|
|
|
** Base coding rules
|
|
|
|
There are a few coding rules across the code base that are enforced by
|
|
lint tools.
|
|
|
|
These rules are:
|
|
|
|
- Opening the =Stdlib= module is not allowed. Inside Base, the OCaml
|
|
stdlib is shadowed and accessible through the =Stdlib= module. We
|
|
forbid opening =Stdlib= so that we know exactly where things come
|
|
from.
|
|
- =Stdlib.Foo= modules cannot be aliased, one must use =Stdlib.Foo=
|
|
explicitly. This is to avoid having to remember a list of aliases
|
|
at the beginning of each file.
|
|
- For some modules that are both in the OCaml stdlib and Base, such as
|
|
=String=, we define a module =String0= for common functions that
|
|
cannot be defined directly in =Base.String= to avoid creating a
|
|
circular dependency. Except for =String= itself, other modules
|
|
are not allowed to use =Stdlib.String= and must use either =String= or
|
|
=String0= instead.
|
|
- Indentation is exactly the one of =ocp-indent=.
|
|
- A few other coding style rules enforced by
|
|
[[https://github.com/janestreet/ppx_js_style][ppx_js_style]].
|
|
|
|
The Base specific coding rules are checked by =ppx_base_lint=, in the
|
|
=lint= subfolder. The indentation rules are checked by a wrapper around
|
|
=ocp-indent= and the coding style rules are checked by =ppx_js_style=.
|
|
|
|
These checks are currently not run by =dune=, but it will soon get a
|
|
=-dev= flag to run them automatically.
|
|
|
|
** Sexp (de-)serializers
|
|
|
|
Most types in Base have ~sexp_of_t~ and ~t_of_sexp~ functions for converting
|
|
between values of that type and their sexp representations.
|
|
|
|
One pair of functions deserves special attention: ~String.sexp_of_t~ and
|
|
~String.t_of_sexp~. These functions have the same types as ~Sexp.of_string~ and
|
|
~Sexp.to_string~ but very different behavior.
|
|
|
|
~String.sexp_of_t~ and ~String.t_of_sexp~ are used to encode and decode strings
|
|
"embedded" in a sexp representation. On the other hand, ~Sexp.of_string~ and
|
|
~Sexp.to_string~ are used to encode and decode the textual form of
|
|
s-expressions.
|
|
|
|
The following example demonstrates the two pairs of functions in action:
|
|
|
|
#+begin_src ocaml
|
|
open! Base
|
|
open! Stdio
|
|
|
|
(* Embed a string in a sexp *)
|
|
|
|
let example_sexp : Sexp.t = List.sexp_of_t String.sexp_of_t [ "hello"; "world" ]
|
|
|
|
let () =
|
|
assert (Sexp.equal example_sexp (Sexp.List [ Sexp.Atom "hello"; Sexp.Atom "world" ]))
|
|
;;
|
|
|
|
let () =
|
|
assert (
|
|
List.equal
|
|
String.equal
|
|
[ "hello"; "world" ]
|
|
(List.t_of_sexp String.t_of_sexp example_sexp))
|
|
;;
|
|
|
|
(* Embed a sexp in text (string) *)
|
|
|
|
let write_sexp_to_file sexp =
|
|
Out_channel.write_all "/tmp/file" ~data:(Sexp.to_string example_sexp)
|
|
;;
|
|
|
|
(* /tmp/file now contains:
|
|
|
|
{v
|
|
(hello world)
|
|
v} *)
|
|
|
|
let () =
|
|
assert (Sexp.equal example_sexp (Sexp.of_string (In_channel.read_all "/tmp/file")))
|
|
;;
|
|
#+end_src
|