I'm learning OCaml, from a little bit of Haskell that I studied for my thesis, and I'm wondering if there is a similar mechanism (type classes) to expresses that two numbers are addable (like a generic for int, float, string, etc). I know that in OCaml type classes don't exist but I'm wondering for similar mechanism to express the same thing
I'm trying to make a module type that have the common "method" add, but without success.
module type Addable = sig
type t
val add : t -> t -> t
val zero : t
val one : t
end
module Addable_int : Addable with type t = int = struct
type t = int
let add a b = a + b
let zero = 0
let one = 1
end
module Addable_float : Addable with type t = float = struct
type t = float
let add a b = a +. b
let zero = 0.
let one = 1.
end
let rec sum (type a) (module A : Addable with type t = a) =
match l with
| [] -> A.zero
| x::l -> A.add x (sum (module A) l)
Update: The function sum give an error: "This expression has type 'a but an expression was expected of type a The type constructor a would escape its scope"
Update-Update:
module type NumberType = sig
type t
val add : t -> t -> t
val zero : t
val one : t
end
module IntNumber : NumberType with type t = int = struct
type t = int
let add x y = x + y
let zero = 0
let one = 1
end
module FloatNumber : NumberType with type t = float = struct
type t = float
let add x y = x +. y
let zero = 0.0
let one = 1.0
end
module Addbl (M: NumberType) = struct
type t = M.t
let add x y = M.add x y
let zero = M.zero
let one = M.one
end
let rec sum : type a. (module NumberType with type t = a) -> a list -> a =
fun (module M) l ->
match l with
| [] -> M.zero
| x::t -> M.add x (sum (module M) t)
let () =
let xs = [1.;2.;3.] in
let _ = print_float @@ sum (module FloatNumber) xs in
let _ = print_string "\n" in
let xsi = [1;2;3;4] in
print_int @@ sum (module IntNumber) xsi
This code work and the sum function is generalized. I don't know if there is a way to eliminate the need to pass (module IntNumber) or (module FloatNumber) in the sum function, to abstract this behaviour
No, OCaml does not have type classes.
But, consider the Float
and Int
modules both implement several common functions, like add
, sub
, mul
, div
, etc.
They also both have a type t
and implement these functions in terms of t
. This allows for a functor to abstract out these behaviors and work much the same way.
# module type NumberType = sig
type t
val add : t -> t -> t
val sub : t -> t -> t
val mul : t -> t -> t
val div : t -> t -> t
end
module Math (N : NumberType) = struct
type t = N.t
let ( + ) = N.add
let ( - ) = N.sub
let ( * ) = N.mul
let ( / ) = N.div
end;;
module type NumberType =
sig
type t
val add : t -> t -> t
val sub : t -> t -> t
val mul : t -> t -> t
val div : t -> t -> t
end
module Math :
functor (N : NumberType) ->
sig
type t = N.t
val ( + ) : t -> t -> t
val ( - ) : t -> t -> t
val ( * ) : t -> t -> t
val ( / ) : t -> t -> t
end
# let open Math (Int) in
45 + 67;;
- : int = 112
# let open Math (Float) in
45.6 + 87.2;;
- : float = 132.8
An example with an Addable
type using functors:
# module type Addable = sig
type t
val nil : t
val add : t -> t -> t
end;;
module type Addable =
sig type t val nil : t val add : t -> t -> t end
# module SumList (A : Addable) = struct
type t = A.t
let sum_list = List.fold_left A.add A.nil
end;;
module SumList :
functor (A : Addable) ->
sig type t = A.t val sum_list : t list -> t end
# let open SumList (struct include Int let nil = 0 end) in
sum_list [1; 2; 3; 4];;
- : int = 10
# let open SumList (struct
include String
let nil = ""
let add = (^)
end) in
sum_list ["foo"; "bar"; "baz"];;
- : string = "foobarbaz"
You might also use the same Addable
module signature with first class modules to avoid the need for the functors, allowing one function to sum lists of different types.
# let sum_list
(type a)
(module A : Addable with type t = a)
(lst : a list) =
List.fold_left A.add A.nil lst;;
val sum_list : (module Addable with type t = 'a) -> 'a list -> 'a = <fun>
# module IntAdd = struct
type t = int
let nil = 0
let add = (+)
end;;
module IntAdd : sig type t = int val nil : t val add : t -> t -> t end
# module StringAdd = struct
type t = string
let nil = ""
let add = (^)
end;;
module StringAdd : sig type t = string val nil : t val add : t -> t -> t end
# module FloatAdd = struct
type t = float
let nil = 0.
let add = (+.)
end;;
module FloatAdd : sig type t = float val nil : t val add : t -> t -> t end
# sum_list (module IntAdd) [1; 2; 3; 4; 5];;
- : int = 15
# sum_list (module StringAdd) ["1"; "2"; "3"; "4"; "5"];;
- : string = "12345"
# sum_list (module FloatAdd) [1.; 2.; 3.; 4.; 5.];;
- : float = 15.