polymorphismocamlmonomorphism

Monomorphic type in pattern matching with default case


Well, actually it's not a problem since I solved it but it bothers me too much :

Let's write this :

test.ml

type bop = Beq  | Bneq | Badd
type value = Vint of int | Vchar of char

let eval bop a b = 
  let op = match bop with
    | Beq -> (=) 
    | Bneq -> (<>) 
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

If I compile it I get :

ocamlc -o test test.ml File "test.ml", line 6, characters 11-62:

Warning 8: this pattern-matching is not exhaustive. Here is an example of a value that is not matched: Badd

Compilation finished at Tue Sep 13 13:24:50

Which is normal, I forgot to add the Badd case.

So, since I hate warnings, I change my code to :

let eval bop a b = 
  let op = match bop with
    | Beq -> (=) 
    | Bneq -> (<>) 
    | _ -> assert false (* or exit 1 or raise Exit *)
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

And then I compile (and, as you can understand, here comes the disturbing part ;-)) and I get :

ocamlc -o test test.ml File "test.ml", line 13, characters 31-33:

Error: This expression has type char but an expression was expected of type int

Compilation exited abnormally with code 2 at Tue Sep 13 13:26:48

Well, what ? I figured out that the type of op was not 'a -> 'a -> bool but '_a -> '_a -> bool and so I changed my code because I remembered about OCaml not allowing polymorphic types for non values and that partial applications are not values. It became :

let eval bop a b = 
  let op a b = match bop with
    | Beq -> a = b 
    | Bneq -> a <> b 
    | _ -> assert false
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

And after I compiled :

ocamlc -o test test.ml

Compilation finished at Tue Sep 13 13:29:48

I could have written :

let eval bop a b = 
  let op = match bop with
    | Beq -> (=) 
    | Bneq -> (<>) 
    | _ -> Obj.magic
  in
  match a, b with
    | Vint i1, Vint i2 -> op i1 i2
    | Vchar c1, Vchar c2 -> op c1 c2
    | _ -> assert false

and it compiles perfectly too (but, eww, Obj.magic is just a nickname to some lowlife OCaml programmer, right ?).

So, here is my question, how come that the compiler changes its behaviour when semantically I wrote exactly the same thing ? (I tested it with several versions of OCaml (3.12.1, 4.01.0, 4.02.3, 4.03.0)).


Solution

  • So, I prefer to answer in a simple way :

    applications are monomorphic !

    Actually, even better :

    If you're not a function declaration, an identifier or a constant, you can't be polymorphic

    But if you know that your type should be polymorphic, there is a way to do it by declaring it as a function. So, another way to make it polymorphic is to write :

    let eval bop a b = 
      let op = match bop with
        | Beq -> (=) 
        | Bneq -> (<>) 
        | _ -> fun _ -> assert false
      in
      match a, b with
        | Vint i1, Vint i2 -> op i1 i2
        | Vchar c1, Vchar c2 -> op c1 c2
        | _ -> assert false
    

    and another link to answer this : http://caml.inria.fr/pub/old_caml_site/FAQ/FAQ_EXPERT-eng.html#polymorphisme