Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can sexplib be used with functor types like Map?

Sexplib's syntax extension makes serialization and deserialization of arbitrary user-defined data structures easy in OCaml. It is generally done by adding a with sexp annotation to the end of a type definition:

type a = A of int | B of float with sexp

This does not seem to generalize directly to functor-based types, nor is it clear how the Sexplib standard type converters can capture even the standard functors.

So far I have worked around this by flattening a specific Map type instance (e.g. int Map.Make(String).t) to a list before serialization, and vice versa, but surely this hasn't been completely overlooked by the generally ambitious authors of Sexplib/Jane Street Core. I also notice that older versions of Batteries mix in custom sexp serialization to their major modules like [Bat]Map, but that this has been removed for some time.

How are Maps or other complex functor types commonly used with Sexplib serialization?

like image 829
jrk Avatar asked Oct 11 '12 23:10

jrk


1 Answers

One way is to define a new functor that takes in the additional information needed to serialize the data. Here's a complete implementation I have used in the past with Batteries. Note I also preferred the exceptionless and labeled version of Map, so I've opened those, but you could of course remove that.

module type SEXPABLE = sig
  type t
  val sexp_of_t : t -> Sexplib.Sexp.t
  val t_of_sexp : Sexplib.Sexp.t -> t
end

module Map = struct

  module type S = sig
    include BatMap.S
    include module type of Labels
    include module type of Exceptionless
    val sexp_of_t : ('a -> Sexplib.Sexp.t) -> 'a t -> Sexplib.Sexp.t
    val t_of_sexp : (Sexplib.Sexp.t -> 'a) -> Sexplib.Sexp.t -> 'a t
  end

  module Make (Ord : BatInterfaces.OrderedType)
              (Sexpable : SEXPABLE with type t = Ord.t)
              : S with type key = Ord.t = struct
    include BatMap.Make(Ord)
    include Labels
    include Exceptionless

    open Sexplib.Sexp
    open Sexplib.Conv

    let sexp_of_t sexp_of_data t =
      let f ~key ~data ans = List [Sexpable.sexp_of_t key; sexp_of_data data] :: ans in
      List (fold ~f ~init:[] t)

    let t_of_sexp data_of_sexp sexp = match sexp with
      | Atom _ -> of_sexp_error "Map.Make(...).t_of_sexp: list needed" sexp
      | List l ->
          let f ans = function
            | List [key_sexp; data_sexp] ->
                let key = Sexpable.t_of_sexp key_sexp in
                let data = data_of_sexp data_sexp in
                add ~key ~data ans
            | List _ | Atom _ ->
                of_sexp_error "Map.Make(...).t_of_sexp: 2-tuple list needed" sexp
          in
          List.fold_left ~f ~init:empty l
  end
end

If I recall correctly, Batteries removed such features to reduce dependencies on additional libraries. Your other option is to use Core, which has these functions out-of-the-box.

like image 108
Ashish Agarwal Avatar answered Nov 14 '22 22:11

Ashish Agarwal