caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] GADT: equal functions typechecks differently
@ 2013-07-19 10:29 Ivan Gotovchits
  2013-07-19 15:32 ` Leo White
  0 siblings, 1 reply; 2+ messages in thread
From: Ivan Gotovchits @ 2013-07-19 10:29 UTC (permalink / raw)
  To: caml-list


I've found a case when two function, that differs very slightly (almost
identical), causes different reactions of a typechecker. For me, this
looks like a bug, but may be there is something (well of course there
is) that I do not understand:

I have two types, an id and a pool of id. Both are protected with
phantom type. The definitions is very small, so I will provide them:

  module Set =
    Set.Make(struct type t = int let compare = compare end)

  type 'a pool = {
    set : Set.t;
    min : int;
    max : int;
  }
  type 'a id = int

  type our
  type ads
  type ows
  type ext


I define two GADT's and two injections:

  type _ sys =
    | Our : our sys
    | Ows : ows sys
    | Ads : ads sys
    | Ext : ext sys

  type tid   = Id : 'a sys * 'a id -> tid
  type tpool = Pool : 'a sys * 'a pool -> tpool

  let to_tid sys id = Id (sys,id)
  let to_tpool sys pool = Pool (sys,pool)

Using common idiom (it is common, isn't?) I can typesafely compare my
systems.

  type (_,_) eq = Eq : ('a,'a) eq

  let try_sys_equal: type a b. (a sys * b sys) -> (a,b) eq option =
   function
   | Our,Our -> Some Eq
   | Ows,Ows -> Some Eq
   | Ads,Ads -> Some Eq
   | Ext,Ext -> Some Eq
   | _       -> None


Next, I provide two injections:

  # let of_tid: type t1. t1 sys -> tid -> t1 id option =
    fun sys1 (Id (sys2,id)) ->
      match try_sys_equal (sys1,sys2) with
      | Some Eq -> Some id
      | None -> None;;

  val of_tid : 't1 sys -> tid -> 't1 id option = <fun>

  # let of_tpool: type t1. t1 sys -> tpool -> t1 pool option =
    fun sys1 (Pool (sys2,pool)) ->
      match try_sys_equal (sys1,sys2) with
      | Some Eq -> Some pool
      | None -> None;;

  val of_tpool : 't1 sys -> tpool -> 't1 pool option = <fun>

As you can see, they typechecks. But here is the strange behavior - if I
write the first function in a slightly different manner, it typechecks:

  # let of_tid: type t1 t2. t1 sys -> tid -> t2 id option =
    fun sys1 (Id (sys2,id)) ->
      match try_sys_equal (sys1,sys2) with
      | Some Eq -> Some id
      | None -> None;;
  val of_tid : 't1 sys -> tid -> 't2 id option = <fun>

But if I add the same t2 type to the second function it fails:

  let of_tpool: type t1 t2. t1 sys -> tpool -> t2 pool option =
    fun sys1 (Pool (sys2,pool)) ->
      match try_sys_equal (sys1,sys2) with
      | Some Eq -> Some pool
      | None -> None;;
          Characters 158-162:
        | Some Eq -> Some pool
                          ^^^^
  Error: This expression has type ex#7 pool
       but an expression was expected of type t2 pool
       Type ex#7 = t1 is not compatible with type t2 


Thank in advance for any clues
Ivan

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [Caml-list] GADT: equal functions typechecks differently
  2013-07-19 10:29 [Caml-list] GADT: equal functions typechecks differently Ivan Gotovchits
@ 2013-07-19 15:32 ` Leo White
  0 siblings, 0 replies; 2+ messages in thread
From: Leo White @ 2013-07-19 15:32 UTC (permalink / raw)
  To: Ivan Gotovchits; +Cc: caml-list

>   type 'a pool = {
>     set : Set.t;
>     min : int;
>     max : int;
>   }
>   type 'a id = int
>

The difference between id and pool is that 'a id is equal to 'b id for
any types 'a and 'b, since they are both equal to int, while 'a pool
only equals 'b pool if 'a equals 'b. For example:

  # let f (x: int id) : float id = x;;
  val f : int id -> float id = <fun>

  # let f (x: int pool) : float pool = x;;
  Characters 35-36:
    let f (x: int pool) : float pool = x;;
                                       ^
  Error: This expression has type int pool
         but an expression was expected of type float pool
         Type int is not compatible with type float 

Interestingly, 'a pool is a subtype of 'b pool for any 'a and 'b, so the
following definition is allowed:

  # let f (x: int pool) : float pool = (x :> float pool);;
  val f : int pool -> float pool = <fun>

This is why:

>   # let of_tid: type t1 t2. t1 sys -> tid -> t2 id option =
>     fun sys1 (Id (sys2,id)) ->
>       match try_sys_equal (sys1,sys2) with
>       | Some Eq -> Some id
>       | None -> None;;
>   val of_tid : 't1 sys -> tid -> 't2 id option = <fun>

is accepted, but 

>   let of_tpool: type t1 t2. t1 sys -> tpool -> t2 pool option =
>     fun sys1 (Pool (sys2,pool)) ->
>       match try_sys_equal (sys1,sys2) with
>       | Some Eq -> Some pool
>       | None -> None;;
>           Characters 158-162:
>         | Some Eq -> Some pool

is disallowed. We could, however, write this function using subtyping:

  # let of_tpool: type t1 t2. t1 sys -> tpool -> t2 pool option =
    fun sys1 (Pool (sys2,pool)) ->
      match try_sys_equal (sys1,sys2) with
      | Some Eq -> Some (pool :> t2 pool)
      | None -> None;;
  val of_tpool : 't1 sys -> tpool -> 't2 pool option = <fun>

Note that these functions are not really using any of the GADT
constraints. They don't need to since any 'a id can be used as a t2 id
and any 'a pool can be subtyped into a t2 pool.

Regards,

Leo

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2013-07-19 15:32 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-07-19 10:29 [Caml-list] GADT: equal functions typechecks differently Ivan Gotovchits
2013-07-19 15:32 ` Leo White

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).