OCaml functions on uncertain data types

63 views Asked by At

I have a data type,

type data = IntData of int list | FloatData of float list

I want to write a function with signature map : data -> ('a -> 'a) -> data which will apply the given function to each element in the data's list.

The compiler doesn't like the following code, for understandable reasons.

let rec map dat func =
  match dat with 
  | IntData il = IntData (List.map func il)
  | FloatData fl = FloatData (List.map func fl)

Of course, the compiler can't make sense of the idea that func could have a different type in each of these cases.

On the other hand, I can't think of how to accomplish this kind of behavior, except to write a separate map function, one for IntData and another for FloatData. But that seems like the kind of wasteful duplication of code that OCaml strives to avoid.

3

There are 3 answers

3
Chris On BEST ANSWER

You probably want a data type that can represent either an int or a float.

type num = Int of i | Float of i

Where data is:

type data = num list

Now your func argument can be a function with type num -> num but not num -> 'a as you want a list of num as the outcome.

Of course, this means you cannot be assured your lists are only of ints or only floats. It would be important to specify why you want to do this as there may be a better solution to the larger problem.

1
Virgile On

While the previous answer is probably the simplest route, if you don't fear using GADT which are admittedly a quite advanced feature of the language, and have the drawback of requiring more type annotation that standard variant types, you can define your data type as one:

type _ data = IntData: int list -> int data | FloatData: float list -> float data;;

then, it becomes possible to indeed indicate the relationship between the type of data contained in data and the type manipulated by func:

let rec map (type a) (dat: a data) (func: a -> a): a data =
    match dat with
    | IntData il -> IntData (List.map func il)
    | FloatData fl -> FloatData (List.map func fl);;

Note that you need the locally abstract type (type a) for the function to typecheck, since a will be instantiated differently in the two branches.

5
Stef On

Your map function first makes a decision on the type of list it's been handed, and then applies a function to that list.

The decision is whether to apply a function to a list of floats or to apply a function to a list of ints.

Presumably, the user will want a different function applied in those two cases! So reflect that in your code:

let map intfun floatfun = function
  | IntData intlist -> IntData (List.map intfun intlist)
  | FloatData floatlist -> FloatData (List.map floatfun floatlist)

Then the user can combine an int->int function with a float->float function, for instance:

let mapPlusThree = map (fun x -> x+3) (fun x -> x +. 3.0)