Artem Koshelev
Artem Koshelev

Reputation: 10607

Why this code works in interactive but fails when compiled?

I'm a complete newbie in OCaml and trying to create a simple console program.

(*let k = read_int()
let l = read_int()
let m = read_int()
let n = read_int()
let d = read_int()*)

let k = 5
let l = 2
let m = 3
let n = 4
let d = 42

let rec total: int -> int -> int = fun i acc ->
  if i > d then
    acc
  else
    if (i mod k) == 0 || (i mod l) == 0 || (i mod m) == 0 || (i mod n) == 0 then
      total (i + 1) (acc + 1)
    else
      total (i + 1) acc;

print_int (total 1 0)

But if I try to compile it, it fails:

PS C:\Users\user> ocamlc -g .\a148.ml
File ".\a148.ml", line 14, characters 2-180:
Warning S: this expression should have type unit.
File ".\a148.ml", line 22, characters 0-21:
Error: This expression has type unit but is here used with type int

So, looks like if expression cannot return value here (why?). I've added let binding

let k = 5
let l = 2
let m = 3
let n = 4
let d = 42

let rec total: int -> int -> int = fun i acc ->
  let x' = if i > d then
      acc
    else
      if (i mod k) == 0 || (i mod l) == 0 || (i mod m) == 0 || (i mod n) == 0 then
    total (i + 1) (acc + 1)
      else
    total (i + 1) acc;
    x'
print_int (total 1 0)

and it works, but raises another error:

File ".\a148.ml", line 23, characters 0-0:
Error: Syntax error

Line 23 is the next to print_int statement and empty, so it seems like compiler wants something else from me, but I don't know what.

UPD: ok, the working code:

let k = 5 in
let l = 2 in
let m = 3 in
let n = 4 in
let d = 42 in
let rec total i acc =
  if i > d then
    acc
  else
    if (i mod k) == 0 || (i mod l) == 0 || (i mod m) == 0 || (i mod n) == 0 then
      total (i + 1) (acc + 1)
    else
      total (i + 1) acc
in let x = total 1 0 in
print_int x;

Upvotes: 2

Views: 216

Answers (1)

pad
pad

Reputation: 41290

The problem is the misuse of semicolon (;).

Semicolon intends to be the sequence composition of two expressions. S1 ; S2 means that the compiler expects S1 to be unit type, computes S1 and S2 in that order and returns the result of S2.

Here you mistakenly use ;, so OCaml expects the second if...then...else to return unit and wants you to provide one more expression. Removing ; and adding necessary in(s) should make the function compile:

let k = 5 in
let l = 2 in
let m = 3 in
let n = 4 in
let d = 42 in

let rec total: int -> int -> int = fun i acc ->
  if i > d then
    acc
  else
    if (i mod k) == 0 || (i mod l) == 0 || (i mod m) == 0 || (i mod n) == 0 then
      total (i + 1) (acc + 1)
    else
      total (i + 1) acc

Regarding your second function, you should replace ; by in to indicate that x' is used to compute the return value.

BTW, your total function looks weird since you use lambda expression in the function body. Explicit declaration is more readable:

let rec total i acc = 
   if i > d then
      acc
   else if i mod k = 0 || i mod l = 0 || i mod m = 0 || i mod n = 0 then
      total (i + 1) (acc + 1)
   else
      total (i + 1) acc

I have also changed reference equality (==) to structural equality (=) though there is no difference among them in integer.

Upvotes: 2

Related Questions