Search code examples
ocamlcompiler-warningsoptional-arguments

Optional argument cannot be erased?


I wanted to have a tail-recursive version of List.map, so I wrote my own. Here it is:

let rec list_map f l ?(accum=[])=
  match l with
      head :: tail -> list_map f tail ~accum:(head :: accum)
    | [] -> accum;;

Whenever I compile this function, I get:

File "main.ml", line 69, characters 29-31:
Warning X: this optional argument cannot be erased.

The tutorial says that this means that I'm trying to create a function with no non-optional arguments. But the function above clearly takes non-optional arguments.

I'm probably just doing something really dumb, but what?


Solution

  • The previous solutions do compile, but won't give the expected result. The function f is never applied to the arguments. A correct code is:

    let rec list_map f ?(accum = []) l = match l with
        | head :: tail -> list_map f ~accum:(f head :: accum) tail
        | [] -> accum;;
    

    The inferred type is:

    val list_map : ('a -> 'b) -> ?accum:'b list -> 'a list -> 'b list = <fun>
    

    ... in contrast to the wrong one:

    val list_map : 'a -> ?accum:'b list -> 'b list -> 'b list = <fun>
    

    Please note, that the result list is reversed:

    # list_map ( ( ** ) 2.) [1.;2.;3.;4.];;
    - : float list = [16.; 8.; 4.; 2.]
    

    ... and equals the function rev_list from the List module:

    # List.rev_map ( ( ** ) 2.) [1.;2.;3.;4.];;
    - : float list = [16.; 8.; 4.; 2.]
    

    So you may want to change your function into:

    let rec list_map f ?(accum = []) l = match l with
        | head :: tail -> list_map f ~accum:(f head :: accum) tail
        | [] -> List.rev accum;;
    

    ... which should be tail-recursive as well (according to the manual) and returns the list in the original order:

    # list_map ( ( ** ) 2.) [1.;2.;3.;4.];;
    - : float list = [2.; 4.; 8.; 16.]