Hello,
While working on Raven with heterogeneous columns of a dataframe, I encountered
let cumsum t name =
match get_column t name with
| Some (Col.P (dtype, tensor)) -> (
match dtype with
| Nx.Float32 ->
let arr : float array = Nx.to_array tensor in
let result = Array.copy arr in
for i = 1 to Array.length result - 1 do
result.(i) <- result.(i - 1) +. result.(i)
done;
Col.P (dtype, Nx.create dtype [| Array.length result |] result)
| Nx.Float64 ->
let arr : float array = Nx.to_array tensor in
let result = Array.copy arr in
for i = 1 to Array.length result - 1 do
result.(i) <- result.(i - 1) +. result.(i)
done;
Col.P (dtype, Nx.create dtype [| Array.length result |] result)
| Nx.Int32 ->
let arr : int32 array = Nx.to_array tensor in
let result = Array.copy arr in
for i = 1 to Array.length result - 1 do
result.(i) <- Int32.add result.(i - 1) result.(i)
done;
Col.P (dtype, Nx.create dtype [| Array.length result |] result)
| Nx.Int64 ->
let arr : int64 array = Nx.to_array tensor in
let result = Array.copy arr in
for i = 1 to Array.length result - 1 do
result.(i) <- Int64.add result.(i - 1) result.(i)
done;
Col.P (dtype, Nx.create dtype [| Array.length result |] result)
| _ -> failwith "cumsum: unsupported numeric type")
| _ -> failwith "cumsum: column must be numeric"
Upon searching for something comparable to templates in C++, I came across type class in Haskell.
anyMax :: MyOrdered a => [a] -> Maybe a
where any type a instantiating MyOrdered works with anyMax.
The closest resources for OCaml I found were: Implementing type-classes as OCaml modules and Haskell type-classes in OCaml and C++ but I liked neither of them. The cleanest solution I thought of is
Edit. Added an illustration of Column motivated by Raven.
Column
type _ col =
| I : int list -> int col
| B : bool list -> bool col
module type Col = sig
type t
val print : t col -> unit
end
(* Integer instance *)
module IntCol : Col with type t = int = struct
type t = int
let print (I lst) = List.iter (fun x -> Printf.printf "%i " x) lst
end
(* Boolean instance *)
module BoolCol : Col with type t = bool = struct
type t = bool
let print (B lst) = List.iter (fun x -> Printf.printf "%B " x) lst
end
(* map witness to corresponding module *)
let colMod_of : type a. a col -> (module Col with type t = a) = function
| I _ -> (module IntCol)
| B _ -> (module BoolCol)
(* abstract printer *)
let anyPrint : type a. a col -> unit =
fun col ->
let (module Mod : Col with type t = a) = colMod_of col in
Mod.print col
(* Example *)
let () =
anyPrint @@ I [1; 2; 3];
print_endline "";
anyPrint @@ B [true; false; true];
print_endline ""
Sorting
(* abstract order *)
module type ORDERED = sig
type t
val compare : t -> t -> int
end
(* integer instance *)
module IntOrd : ORDERED with type t = int = struct
type t = int
let compare = compare
end
(* string instance *)
module StringOrd : ORDERED with type t = string = struct
type t = string
let compare = compare
end
(* type witness *)
type _ ty =
| Int : int ty
| String : string ty
(* map witness to corresponding module *)
let ord_of : type a. a ty -> (module ORDERED with type t = a) = function
| Int -> (module IntOrd)
| String -> (module StringOrd)
(* abstract sorting *)
let anySort : type a. a ty -> a list -> a list =
fun ty lst ->
let (module Ord : ORDERED with type t = a) = ord_of ty in
List.sort Ord.compare lst
(* example *)
let () =
let sorted_ints = anySort Int [5; 1; 3; 2] in
sorted_ints |> [%show: int list] |> Printf.printf "%s\n";
let sorted_strings = anySort String ["cat"; "dog"; "apple"] in
sorted_strings |> [%show: string list] |> Printf.printf "%s\n";
Discussion.
- What is your subjective opinion of my solution?
- How far does it align with the design philosophy of OCaml?
- How do OCaml programmers deal with the problem I faced?
- Do you recommend any resource for design patterns in OCaml?
Edit. The discussion is about the design pattern in general, not sorting or Raven.