0

Sorry about the "what am I missing here" style of question here, but I'm just missing something here.

I was trying to understand how GADTs work in OCaml, I define the following (in utop):

 type value =
| Bool : bool -> value
| Int : int -> value
;;

type _ value =
| Bool : bool -> bool value
| Int : int -> int value
;;

type _ expr =
| Value : 'a value -> 'a expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
| Lt : 'a expr * 'a expr -> bool expr
| Eq : 'a expr * 'a expr -> bool expr
| Gt : 'a expr * 'a expr -> bool expr
;;

I defined an eval function:

let rec eval : type a. a expr -> a = function
| Value (Int i) -> i
| Value (Bool b) -> b
| Lt (a, b) -> (eval a) < (eval b)
| Gt (a, b) -> (eval a) > (eval b)
| Eq (a, b) -> (eval a) = (eval b)
| If (c, a, b) -> if eval c then (eval a) else (eval b)
;;

but got an error:

Line 4, characters 15-23:
Error: This expression has type $Lt_'a but an expression was expected of type
         int

What exactly does this mean?

Just to test further, I modified the expression GADT to be:

type _ expr =
| Value : 'a value -> 'a expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
| Lt : int expr * int expr -> bool expr
| Eq : 'a expr * 'a expr -> bool expr
| Gt : int expr * int expr -> bool expr
;;

and then I see

Line 6, characters 15-23:
Error: This expression has type $Eq_'a but an expression was expected of type
         int

When I finally modify it to be

type _ expr =
| Value : 'a value -> 'a expr
| If : bool expr * 'a expr * 'a expr -> 'a expr
| Lt : int expr * int expr -> bool expr
| Eq : int expr * int expr -> bool expr
| Gt : int expr * int expr -> bool expr
;;

it works fine.

Update (more context):

  • Ocaml version: 4.08.1
  • Libraries opened during this session: Base

Update (solution):

  • it turned out to be (as mentioned in the first line of the selected answer) because I had previously, within utop run open Base ;;
  • In a fresh session I'm able to enter the types initially mentioned and eval is happy with that.
agam
  • 5,064
  • 6
  • 31
  • 37

1 Answers1

2

The direct cause of the error is that you are using a library (maybe Base or Core?) that shadows the polymorphic comparison operators (<,<=,=,>=,>) and replace them with integer comparison operators.

Concerning the error message, when you pattern match a GADT constructor with existential types,

| Lt (a, b) -> (eval a) < (eval b)

the typechecker introduces new types to represent the existential types. Here, in the (original) definition of Lt,

| Lt : 'a expr * 'a expr -> bool expr

there is one existentially quantified type variable: 'a.

When pattern matching on Lt, we need to replace this type variable with a new type. Moreover, it is quite useful in error message to try to pick a meaningful name for this type. To do so, the typechecker constructs a new type name piece by piece as $ + Lt + 'a:

  • $: to mark an existential type
  • Lt: to indicate that it was introduced by the constructor Lt
  • a: to remember that the existential type variable was named 'a in the definition of the constructor

In other words, in the pattern match above, we have something akin to

| Lt ( (a: $Lt_'a eval), (b: $Lt_'a eval)) -> (eval a) < (eval b)

And when typing:

  (eval a) < (eval b)

the typechecker compare the type of <: int -> int with the type of eval a: $Lt_'a and outputs your original error message:

 Line 4, characters 15-23:
 Error: This expression has type $Lt_'a but an expression was expected of type
     int
octachron
  • 17,178
  • 2
  • 16
  • 23