Col


Intro

 

Col is a syntax library for OCaml for a compact and safe definition of IO functions on lists of flat records.

 

One type definition in a record-like syntax is expanded into a regular type definition of a record type, a tuple type and a class, plus many conversion functions which would have to be written manually otherwise. List of such objects can be loaded from and saved to CSV files with a header describing each field (column). You can edit these files manually, possibly by inserting new columns, and yet these files remain readable by your program.

 

See http://martin.jambon.free.fr/col-doc.html for an illustration of a program and the result after preprocessing. Hopefully it is self-explanatory. You can notice that the interface (doc.mli) which is automatically generated is much longer than the source implementation (doc.ml ~80 lines). After preprocessing, the implementation (doc.ppo) is truly huge (~800 lines), so it might take a while to compile.

 

Documentation

 

There is no documentation but there is a complete example at http://martin.jambon.free.fr/col-doc.html

 

Changes

 

version 1.1.1:

 

version 1.1.0: added functions for custom sort. Modules which are used to define custom types must now define a comparison function named "compare" (use "let compare = compare" if you want to use OCaml's default or if you don't want to use the comparison functions).

 

Examples

 

Here is a short example which defines a list of 2 records and saves them:

 

(* File test.ml *)
type col t = { x : float;
               y : float;
               title "The Title" : string = "no title" }

let data =
  let x = 1. in
  [ T.create ~x ~y:2. ();
    T.create ~y:4. ~x ~title:"line2" () ]

let _ = T.save_csv "data.csv" data

$ ocamlfind ocamlopt -o testcol test.ml -syntax camlp4o -package col -linkpkg
$ ./testcol
$ cat data.csv
x,y,"The Title"
1.,2.,"no title"
1.,4.,line2
$

 

 

Bugs

 

Installation

 

There is a bug in OCaml 3.09.1 which prevents from pr_o.cmo from working correctly (Stack overflow while executing something like "camlp4o pr_o.cmo ..."). The bug is not present in 3.08.4 and has been fixed in 3.09.2. If you are having this problem, compile with:

make common bc nc

(instead of "make" alone)

 

User Questions

 

Could you give an example of loading col from the toplevel using ocamlfind?

 

A: Like other findlib packages that provide a camlp4 syntax extension, you have to specify manually that you want to use "camlp4o", which correspond to the regular OCaml syntax support in camlp4. Actually I don't know if it would be possible to define a findlib package or sub-package that would do this automatically. Anyway the steps are the following:

 

Note that if you are just interested in knowing which types and functions are made available by a given "type col" definition, using ocamlc or ocamlopt with the -i option may be a better choice than using the toplevel, e.g.

ocamlfind ocamlopt -i -syntax camlp4o -package col yourfile.ml > yourfile.mli

 

Here is a simple toplevel session; as you can see, the output is long:

        Objective Caml version 3.08.4

# #use "topfind";;             
- : unit = ()
Findlib has been successfully loaded. Additional directives:
  #require "package";;      to load a package
  #list;;                   to list the available packages
  #camlp4o;;                to load camlp4 (standard syntax)
  #camlp4r;;                to load camlp4 (revised syntax)
  #predicates "p,q,...";;   to set these predicates
  Topfind.reset();;         to force that packages will be reloaded
  #thread;;                 to enable threads 

- : unit = ()
# #camlp4o;;
/home/martin/godi/lib/ocaml/std-lib/camlp4: added to search path
/home/martin/godi/lib/ocaml/std-lib/camlp4/camlp4o.cma: loaded
        Camlp4 Parsing version 3.08.4

# #require "col";;             
/home/martin/godi/lib/ocaml/site-lib/col: added to search path
/home/martin/godi/lib/ocaml/site-lib/col/pa_col.cmo: loaded
/home/martin/godi/lib/ocaml/site-lib/col/run_col.cmo: loaded
# type col point = { x : float; y : float };;
 type point = { x : float; y : float; }
 module Point :
  sig
    val create : x:float -> y:float -> unit -> point
    val create_tuple : x:float -> y:float -> unit -> float * float
    class obj :
      x:float ->
      y:float ->
      unit ->
      object
        val y : float
        val x : float
        method x : float
        method y : float
      end
    exception Bad_format
    val x_of_string : string -> float
    val x_to_string : float -> string
    val y_of_string : string -> float
    val y_to_string : float -> string
    type t = point
    module Index : sig val x : int val y : int end
    module Compare : sig val x : 'a -> 'a -> int val y : 'a -> 'a -> int end
    val fields : string array
    val labels : string array
    val of_array : string array -> point
    val to_array : point -> string array
    exception Invalid_field of string
    exception Invalid_label of string
    val type_float_field : string -> t -> float
    val type_float_label : string -> t -> float
    val load_csv_rows :
      ?strict:bool -> ?noheader:bool -> string -> (point -> unit) -> unit
    val load_csv :
      ?strict:bool -> ?noheader:bool -> ?rev:bool -> string -> point list
    val open_out_csv :
      ?sep:char ->
      ?noheader:bool -> string -> (point -> unit) * (unit -> unit)
    val save_csv_rows :
      ?sep:char -> ?noheader:bool -> string -> (unit -> point option) -> unit
    val save_csv :
      ?sep:char -> ?noheader:bool -> string -> point list -> unit
    val compare_fields :
      [< `Custom of point -> point -> int | `Down of string | `Up of string ]
      list -> point -> point -> int
    val compare_labels :
      [< `Custom of point -> point -> int | `Down of string | `Up of string ]
      list -> point -> point -> int
    module Tup :
      sig
        type t = float * float
        val create : x:float -> y:float -> unit -> float * float
        val of_array : string array -> float * float
        val to_array : float * float -> string array
        val of_record : point -> t
        val to_record : t -> point
        val get_x : t -> float
        val get_y : t -> float
        exception Invalid_field of string
        exception Invalid_label of string
        val type_float_field : string -> t -> float
        val type_float_label : string -> t -> float
        val load_csv_rows :
          ?strict:bool ->
          ?noheader:bool -> string -> (float * float -> unit) -> unit
        val load_csv :
          ?strict:bool ->
          ?noheader:bool -> ?rev:bool -> string -> (float * float) list
        val open_out_csv :
          ?sep:char ->
          ?noheader:bool ->
          string -> (float * float -> unit) * (unit -> unit)
        val save_csv_rows :
          ?sep:char ->
          ?noheader:bool ->
          string -> (unit -> (float * float) option) -> unit
        val save_csv :
          ?sep:char ->
          ?noheader:bool -> string -> (float * float) list -> unit
        val compare_fields :
          [< `Custom of t -> t -> int | `Down of string | `Up of string ]
          list -> t -> t -> int
        val compare_labels :
          [< `Custom of t -> t -> int | `Down of string | `Up of string ]
          list -> t -> t -> int
      end
    module OO :
      sig
        class t : x:float -> y:float -> unit -> obj
        val create : x:float -> y:float -> unit -> t
        val of_array : string array -> t
        val to_array : < x : float; y : float; .. > -> string array
        val of_record : point -> t
        val to_record : < x : float; y : float; .. > -> point
        exception Invalid_field of string
        exception Invalid_label of string
        val type_float_field : string -> t -> float
        val type_float_label : string -> t -> float
        val load_csv_rows :
          ?strict:bool -> ?noheader:bool -> string -> (t -> unit) -> unit
        val load_csv :
          ?strict:bool -> ?noheader:bool -> ?rev:bool -> string -> t list
        val open_out_csv :
          ?sep:char ->
          ?noheader:bool ->
          string -> (< x : float; y : float; .. > -> unit) * (unit -> unit)
        val save_csv_rows :
          ?sep:char ->
          ?noheader:bool ->
          string -> (unit -> < x : float; y : float; .. > option) -> unit
        val save_csv :
          ?sep:char ->
          ?noheader:bool ->
          string -> < x : float; y : float; .. > list -> unit
        val compare_fields :
          [< `Custom of
               (< x : 'b; y : 'c; .. > as 'a) ->
               (< x : 'b; y : 'c; .. > as 'd) -> int
           | `Down of string
           | `Up of string ]
          list -> 'a -> 'd -> int
        val compare_labels :
          [< `Custom of
               (< x : 'b; y : 'c; .. > as 'a) ->
               (< x : 'b; y : 'c; .. > as 'd) -> int
           | `Down of string
           | `Up of string ]
          list -> 'a -> 'd -> int
      end
  end
#