 # Use of functors to approximate F# statically resolved type parameters

Hi,

I am learning OCaml coming from F#. In F#, to calculate the average of an array whose element type supports addition and division, one can write

``````let inline average (arr: 'a[]) : 'a
when ^a : (static member DivideByInt : ^a * int -> ^a)
and  ^a : (static member (+) : ^a * ^a -> ^a)
and  ^a : (static member Zero : ^a)
=
if Array.length arr = 0 then (LanguagePrimitives.GenericZero) else
LanguagePrimitives.DivideByInt (Array.fold (+) (LanguagePrimitives.GenericZero) arr) (Array.length arr)
``````

My understanding is that in OCaml, one would have a module type like so:

``````module type Averagable = sig
type 'a t

val divide_by_int : 'a -> int -> 'a
val plus : 'a -> 'a -> 'a
val zero : 'a
end
``````

My question is how the corresponding function would be written:

``````let average arr =
???
``````

First, `Averagable` should look like this:

``````module type Averagable = sig
type t
val divide_by_int : t -> int -> t
val plus : t -> t -> t
val zero : t
end
``````

Then `average` will look something like this:

``````let average (type t) (module A : Averagable with type t = t) (arr : t array) : t =
Array.fold ~init:A.zero ~f:A.plus arr
``````

(The code above uses Jane Street’s Base/Core library.)

3 Likes

While @smolkaj’s answer is a correct and direct implementation of your F# code, it might be nicer if your code can interplay with existing abstractions in the OCaml infrastructure. For example,

`````` open Base

let average (type a) (module T : Floatable.S with type t = a) xs =
Array.fold ~init:0. ~f:(fun s x -> s +. T.to_float x) xs /.
Float.of_int (Array.length xs)
``````

and now it could be used with any existing numeric data in Base/Core

``````average (module Int) [|1;2;3;4|];;
- : Base.Float.t = 2.5
``````

``````let average_length = average (module struct
include String
let to_float x = Float.of_int (String.length x)
let of_float _ = assert false
end)
``````

The latter example shows that we requested more interface than need, a cost that we have to pay for using an existing definition. In cases when it matters, you can specify the specific interface, e.g.,

``````module type Floatable = sig
type t
val to_float : t -> float
end

let average (type a) (module T : Floatable with type t = a) xs =
Array.fold ~init:0. ~f:(fun s x -> s +. T.to_float x) xs /.
Float.of_int (Array.length xs)
``````

But we reached the point where using first class modules is totally unnecessary. Our interface has only one function, so the following definition of `average`, is much more natural

``````let average xs ~f =
Array.fold ~init:0. ~f:(fun s x -> s +. f x) xs /.
Float.of_int (Array.length xs)
``````

it has type `'a array -> f:('a -> float) -> float` and computes an average of `f x_i` for all elements in the array.

4 Likes