Dan Greenhalgh
Dan Greenhalgh

Reputation: 13

Why can't I pass these two lists into a recursive function in OCaml?

I'm trying to write a simpler parser in OCaml but I have run into a problem I can't get past. I have simplified the code to a bare minimum example, which gives me the same error:

I want to write a function in OCaml that takes in a list of integers and outputs a list of strings. The rules are that if there are two 1s in a row then it outputs "eleven", if there is only one 1 it outputs "one" and for any other number it outputs "other". This python code does the job:

def convert(numbers, names):
    if len(numbers) == 0:
      return names
    n = numbers.pop(0)
    if n == 1:
        if len(numbers) > 0 and numbers[0] == 1:
            numbers.pop(0)
            names.append("eleven")
        else:
            names.append("one")
    else:
        names.append("other")
    return convert(numbers, names)

convert([1, 1, 2, 1, 3, 1], [])
# -> ['eleven', 'other', 'one', 'other', 'one']

My attempt in OCaml is this:

let rec convert (numbers : int list) (names : string list) = function
  | [] -> List.rev names
  | 1 :: 1 :: t -> convert t ("eleven" :: names)
  | 1 :: t -> convert t ("one" :: names)
  | _ :: t -> convert t ("other" :: names)
;;

convert [1; 1; 2; 3] [];;

What I think will happen is convert will call itself recursively: the list of integers will be get smaller and the list of strings bigger until there are no more integers left:

convert [1; 1; 2; 1; 3; 1] []
-> convert [2; 1; 3; 1] ["eleven"]
  -> convert [1; 3; 1] ["other"; "eleven"]
    -> convert [3; 1] ["one"; "other"; "eleven"]
      -> convert [1] ["other"; "one"; "other"; "eleven"]
        -> convert [] ["one"; "other"; "one"; "other"; "eleven"]
          -> ["eleven"; "other"; "one"; "other"; "one"]

But what actually happens is a compilation error:

Error: This expression has type int list -> string list
       but an expression was expected of type string list

highlighting the text convert t ("eleven" :: names)

What's going on here? I don't understand why this doesn't work.

Upvotes: 1

Views: 425

Answers (1)

glennsl
glennsl

Reputation: 29106

function | ... is actually short-hand for fun x -> match x with | .... That is:

let convert numbers names = function
  | ...

is equivalent to

let convert numbers names something_else =
  match something_else with
  | ...

Your convert function therefore expects three arguments, and convert t ("eleven" :: names) returns a function int list -> string list rather than just a string list as inferred from the first branch.

Replace function with match numbers with to have it compile:

let rec convert (numbers : int list) (names : string list) =
  match numbers with
  | [] -> List.rev names
  | 1 :: 1 :: t -> convert t ("eleven" :: names)
  | 1 :: t -> convert t ("one" :: names)
  | _ :: t -> convert t ("other" :: names)
;;

convert [1; 1; 2; 3] [];;

Upvotes: 5

Related Questions