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):
4.08.1
Base
Update (solution):
utop
run open Base;;
eval
is happy with that. 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
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.