Bonjour les chameaux! / Hello camelers!
I would like to convert sqlite3-ocaml returns from Sqlite3.Data.t array to plain ocaml types in a tuple. I guess unwrapping the Data.t can be done using a GADT, here’s my very very first attempt:
(* simulate Sqlite3.Data.t *)
type t =
| NONE
| NULL
| INT of int64
| FLOAT of float
| TEXT of string
| BLOB of string ;;
(* a simple GADT to unwrap Sqlite3.Data.t *)
type _ dbval =
| INT : int64 -> int64 dbval
| FLOAT : float -> float dbval
| TEXT : string -> string dbval
| BLOB : string -> string dbval
| NONE | NULL ;;
let unwrap_data : type a. a dbval -> a = fun dbval ->
match dbval with
| INT x -> x
| FLOAT x -> x
| TEXT str -> str
| BLOB str -> str ;;
let tuple_of_array4 (arr: t array) =
assert (Array.length arr = 4) ;
(unwrap_data arr.(0), unwrap_data arr.(1), unwrap_data arr.(2), unwrap_data arr.(3)) ;;
Compilation fails with this typing error:
File "database.ml", line 233, characters 17-24:
233 | (unwrap_data arr.(0), unwrap_data arr.(1), unwrap_data arr.(2), unwrap_data arr.(3)) ;;
^^^^^^^
Error: This expression has type t but an expression was expected of type
'a dbval
What am I doing wrong? I need to make type t compatible with type 'a dbval.
Thanks in advance.
2 Likes
You cannot make the type t
and 'a dbval
compatible, there are different types.
A very important point to keep in mind with GADTs is that one cannot create type-level information from dynamical values. In other words, there are no functions of type x : t -> f(x) dbval
that will infer the type of its return from the value of its argument in OCaml.
Thus the type of the final result must come from your code source rather than from the dynamical data.
For instance, you can define constructor from the type t
to the right dbval
type:
exception Type_error
let int: t -> _ dbval = function
| INT x -> INT x
| _ -> raise Type_error
let float: t -> _ dbval = function
| FLOAT x -> FLOAT x
| _ -> raise Type_error
Then if you know the type of the tuple, you can write it as:
let tuple_of_array4 (arr: t array) =
assert (Array.length arr = 4) ;
int arr.(0), int arr.(1), int arr.(2), int arr.(3)
or possibly as
let int4 = int, int, int, int
let tuple (a,b,c,d) arr =
assert (Array.length arr = 4) ;
a arr.(0), b arr.(1), c arr.(2), d arr.(3)
There are more complex alternatives based on type witness, that allow to implement a form of static matching over the dynamical type of data, but the core idea that the types are always present in the source code in some way is the same.
1 Like
I think we need to re-examine your actual goal with this approach. The pertinent question is: why do you want to convert from Sqlite3.Data.t
array to ‘plain OCaml types in a tuple’? What do you achieve with that?
Oh I didn’t noticed it would be dynamical typing! I’m too used to ppx (and previously camlp4) written db abstraction layer!
I’m simply replacing sqlexpr by plain sqlite3-ocaml in some existing code of mine. sqlexpr quick doco:
But I can live with a Data.t array!
Everybody has their favourite way of wrapping SQLite. Here’s mine (no PPX): GitHub - yawaramin/ocaml_sql_query: PoC of functional-style SQL query
It has a little data translation layer to convert from Data.t
array to the desired return type.
1 Like