Schemas

Every Irmin store has a schema. This specifies the concrete implementations of the various kinds of customisable aspects of the store. For example what type are the keys, the branches and the contents.

In Irmin everything gets specified upfront using functor application. Whereas with something like Hashtbl its type will be inferred but its use.

Most stores take a Schema module to provide the concrete implementation of the various customisable parts of the Irmin store. This can be a little verbose, but most of the time the defaults are just fine.

module Schema : Irmin.Schema.S = struct
  open Irmin
  module Hash = Hash.BLAKE2B
  module Info = Info.Default
  module Branch = Branch.String
  module Path = Path.String_list
  module Metadata = Metadata.None
  module Contents = struct
    type t = int[@@deriving irmin]
    let merge = Irmin.Merge.(option @@ default t) 
  end
end
module S = Irmin_mem.Make (Schema)
(* A fake clock and info function for our commits *)
let clock = let time = ref 0L in fun () -> time := Int64.add !time 1L; !time
let info () = S.Info.v (clock ())

Here we've defined a schema to use Blake2B hash functions, default commit information, branches are strings, keys (or paths) are string lists, no extra metadata and finally the content of the store is integers.

However, we'll quickly run into problems as soon as we try to use the store.

# let config = Irmin_mem.config () in
  let* repo = S.Repo.v config in
  let* main = S.main repo in
  let* () = S.set_exn ~info main [ "hello" ] 1 in
  S.get main [ "hello" ];;
Line 4, characters 34-45:
Error: This expression has type 'a list
       but an expression was expected of type S.path

This is because in our definition of the schema we hid the implementation details by adding : Irmin.Schema.S. So either we canleave that part out or we must provide a module type to expose the information (or at the very least the bits we need).

module type String_list_int_schema = Irmin.Schema.S 
   with type Hash.t = Irmin.Schema.default_hash
    and type Branch.t = string
    and type Info.t = Irmin.Info.default
    and type Metadata.t = unit
    and type Path.step = string
    and type Path.t = string list
    and type Contents.t = int

And then use that to define the store.

module Schema2 : String_list_int_schema = struct
  open Irmin
  module Hash = Hash.BLAKE2B
  module Info = Info.Default
  module Branch = Branch.String
  module Path = Path.String_list
  module Metadata = Metadata.None
  module Contents = struct
    type t = int[@@deriving irmin]
    let merge = Irmin.Merge.(option @@ default t) 
  end
end
module S = Irmin_mem.Make (Schema2)
let info () = S.Info.v (clock ())

And now the compiler knows what the types are (they haven't been abstracted away).

# let config = Irmin_mem.config () in
  let* repo = S.Repo.v config in
  let* main = S.main repo in
  let* () = S.set_exn ~info main [ "hello" ] 1 in
  S.get main [ "hello" ];;
- : int = 1