views:

88

answers:

3

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?

+3  A: 

You need a non-optional argument after the optional one. Just change the order of the arguments of your function:

let rec list_map f ?(accum=[]) l=
  match l with
    head :: tail -> list_map f  ~accum:(head :: accum) tail
  | [] -> accum;;
Pascal Cuoq
Thank you very much. That one's going to trip me up a lot. I'm used to making optional arguments be last like Python and C++ requires. :-(
Jason Baker
+3  A: 

Yeah your non-optional argument can't be last, because since OCaml supports partial applications, a function missing a last optional argument will just look like a partially-applied function which is still looking for the optional argument. The only way for it to tell that you don't intend to provide the optional argument is that it sees that you have provided an argument after it.

If you have to have it last, you can put a dummy unit argument after it:

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

But in this case yeah changing the order would be better.

newacct
+2  A: 

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.]
simon04