caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* [Caml-list] Request for feedback: A problem with injectivity and GADTs
@ 2013-04-28  0:02 Jacques Garrigue
  2013-04-28  2:45 ` Markus Mottl
                   ` (4 more replies)
  0 siblings, 5 replies; 37+ messages in thread
From: Jacques Garrigue @ 2013-04-28  0:02 UTC (permalink / raw)
  To: OCaML List Mailing

Some of you may already be aware of PR#5985
http://caml.inria.fr/mantis/view.php?id=5985

To explain very shortly what is happening, a bug was found in the ocaml
compiler, both 4.00 and the development version, such that one that
convert a value from any type to any other type, bypassing all checks.
This is what we call a soundness bug, a hole in the type system.

Such problems happen at times, and we are usually very fast to fix them.
However this time the problem is bit more subtle, because fixing it
straightforwardly may make impossible to write a whole category of
GADTs. Hence this request for feedback.

Practical description of the problem:

It is quite usual to write type witnesses using a GADT:

type _ ty =
  | Int : int ty
  | Pair : 'a ty * 'b ty -> ('a * 'b) ty
  | Hashtbl : 'a ty * 'b ty -> ('a, 'b) Hashtbl.t ty

This looks fine, but now suppose that we write the following code:

module F (X : sig type 'a t end) = struct
   type _ ty =
    | Int : int ty
    | Pair : 'a ty * 'b ty -> ('a * 'b) ty
    | T : 'a ty -> 'a X.t ty
end

The idea is to create a type witness with the
type constructor X.t instead of Hashtbl.t.
Now I can use it this way:

module U = struct type 'a t = unit end
module M = F(U)
let w = M.T M.Int
(* val w : int U.t M.ty *)
let w' = match w with M.T x -> x | _ -> assert false
(* val w' : '_a M.ty = M.Int *)

What this means is that we can now give an arbitrary type
to a witness for int. You can easily use it to build a function
from int to any  type:

let f : type a. a M.ty -> int -> a =
  fun w x -> match w with M.Int -> x | _ -> assert false;;
let g : int -> float = f w';;

If we look at the source of the problem, it lies in the fact U.t is not injective.
I.e. when we define a GADT, we assume that all the type variables in
the return type can be determined from the type parameter.
That is, from (int U.t M.ty) we assume that the type of the argument of T is (int M.ty).
However, since U.t is not injective, int U.t is actually equal to any type 'a U.t,
and we can convert.
Note that, for known types, injectivity was already checked.
I.e., the following definition is not accepted:

type _ ty =
  | Int : int ty
  | Pair : 'a ty * 'b ty -> ('a * 'b) ty
  | T : 'a ty -> 'a U.t ty

However, abstract types were assumed to be injective.
Here we see that you cannot take this for granted.

The problem is of course not limited to type witnesses, and not even GADTs:
types with constrained parameters (introduced at the very beginning of ocaml),
can also trigger it.
And you don't need a functor to trigger it: once you know that two types are equal
in some scope, there are many ways to leak that information.

=============================
Here comes the request for feedback:

The fix is simple enough: we should track injectivity, and assume that abstract
types are not injective by default.
However, this also means that the first type I defined above (using Hashtbl.t)
will be refused.

How many of you are using GADTs in this way, and how dependent are you on
abstract types ?


If we need to be able to define GADTs relying on the injectivity of some abstract
types, a straightforward solution is to add injectivity annotations on type parameters,
the same way it was done for variance:

  type #'a t

would mean that t is injective.
This is implemented in branches/non-vanishing, in the subversion repository.
	svn checkout http://caml.inria.fr/svn/branches/non-vanishing
Note that in that branch Hashtbl.t in the standard library is marked as injective.
This introduces some new syntax, and libraries have to be modified to benefit,
so the question is do we really need it?


Jacques Garrigue

P.S.
If you are curious about other difficulties of GADTs, the is also branches/abstract-new
which introduces a notion of new types, either concrete or abstract, which are guaranteed
to be distinct from each other, and any other concrete type. This is useful when
checking the exhaustiveness of pattern-matching, as we can then discard impossible
branches. That branch is completely experimental.


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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-28  0:02 [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
@ 2013-04-28  2:45 ` Markus Mottl
  2013-04-28 10:28   ` Jacques Garrigue
  2013-04-28  5:54 ` Jacques Le Normand
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 37+ messages in thread
From: Markus Mottl @ 2013-04-28  2:45 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

On Sat, Apr 27, 2013 at 8:02 PM, Jacques Garrigue
<garrigue@math.nagoya-u.ac.jp> wrote:
> The fix is simple enough: we should track injectivity, and assume that abstract
> types are not injective by default.
> However, this also means that the first type I defined above (using Hashtbl.t)
> will be refused.

If I understand your proposal correctly, the first type would only be
refused unless the standard Hashtbl module were updated with
appropriate injectivity annotations.  The type variables in the GADT
would not need any annotations, because it is assumed that they have
to be injective there anyway for soundness.

> How many of you are using GADTs in this way, and how dependent are you on
> abstract types ?

This seems like a "one percenter" problem.  OTOH, I guess most people
also rarely run into problems where variance annotations matter, but
if they do, they have no option.  The question should therefore rather
be: what's the alternative?  Are there workarounds?  If not,
annotations may just be a necessity to support advanced use cases.

> If we need to be able to define GADTs relying on the injectivity of some abstract
> types, a straightforward solution is to add injectivity annotations on type parameters,
> the same way it was done for variance:
>
>   type #'a t
>
> would mean that t is injective.

Seems concise, but I'm just a little worried that we may be raising
the bar for beginners again.  The standard library would have to be
updated with another annotation that may seem as obscure to beginners
as variance annotations.  I hope we'll never have to explain to a
beginner the meaning of $%^&#-'a.  Maybe a more verbose type
constraint language would have been the better design choice here, but
I guess we've already left this path with variance annotations.

> If you are curious about other difficulties of GADTs, the is also branches/abstract-new
> which introduces a notion of new types, either concrete or abstract, which are guaranteed
> to be distinct from each other, and any other concrete type. This is useful when
> checking the exhaustiveness of pattern-matching, as we can then discard impossible
> branches. That branch is completely experimental.

I haven't looked at this feature yet, but would this require even more
annotations in the standard library?  If so, maybe it would indeed be
time to think about a more accessible syntax for type constraints.

Regards,
Markus

--
Markus Mottl        http://www.ocaml.info        markus.mottl@gmail.com

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-28  0:02 [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
  2013-04-28  2:45 ` Markus Mottl
@ 2013-04-28  5:54 ` Jacques Le Normand
  2013-04-29  3:45 ` Ivan Gotovchits
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 37+ messages in thread
From: Jacques Le Normand @ 2013-04-28  5:54 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

[-- Attachment #1: Type: text/plain, Size: 4709 bytes --]

do we need the injectivity annotation at the parameter level or at the type
declaration level?


On Sat, Apr 27, 2013 at 5:02 PM, Jacques Garrigue <
garrigue@math.nagoya-u.ac.jp> wrote:

> Some of you may already be aware of PR#5985
> http://caml.inria.fr/mantis/view.php?id=5985
>
> To explain very shortly what is happening, a bug was found in the ocaml
> compiler, both 4.00 and the development version, such that one that
> convert a value from any type to any other type, bypassing all checks.
> This is what we call a soundness bug, a hole in the type system.
>
> Such problems happen at times, and we are usually very fast to fix them.
> However this time the problem is bit more subtle, because fixing it
> straightforwardly may make impossible to write a whole category of
> GADTs. Hence this request for feedback.
>
> Practical description of the problem:
>
> It is quite usual to write type witnesses using a GADT:
>
> type _ ty =
>   | Int : int ty
>   | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>   | Hashtbl : 'a ty * 'b ty -> ('a, 'b) Hashtbl.t ty
>
> This looks fine, but now suppose that we write the following code:
>
> module F (X : sig type 'a t end) = struct
>    type _ ty =
>     | Int : int ty
>     | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>     | T : 'a ty -> 'a X.t ty
> end
>
> The idea is to create a type witness with the
> type constructor X.t instead of Hashtbl.t.
> Now I can use it this way:
>
> module U = struct type 'a t = unit end
> module M = F(U)
> let w = M.T M.Int
> (* val w : int U.t M.ty *)
> let w' = match w with M.T x -> x | _ -> assert false
> (* val w' : '_a M.ty = M.Int *)
>
> What this means is that we can now give an arbitrary type
> to a witness for int. You can easily use it to build a function
> from int to any  type:
>
> let f : type a. a M.ty -> int -> a =
>   fun w x -> match w with M.Int -> x | _ -> assert false;;
> let g : int -> float = f w';;
>
> If we look at the source of the problem, it lies in the fact U.t is not
> injective.
> I.e. when we define a GADT, we assume that all the type variables in
> the return type can be determined from the type parameter.
> That is, from (int U.t M.ty) we assume that the type of the argument of T
> is (int M.ty).
> However, since U.t is not injective, int U.t is actually equal to any type
> 'a U.t,
> and we can convert.
> Note that, for known types, injectivity was already checked.
> I.e., the following definition is not accepted:
>
> type _ ty =
>   | Int : int ty
>   | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>   | T : 'a ty -> 'a U.t ty
>
> However, abstract types were assumed to be injective.
> Here we see that you cannot take this for granted.
>
> The problem is of course not limited to type witnesses, and not even GADTs:
> types with constrained parameters (introduced at the very beginning of
> ocaml),
> can also trigger it.
> And you don't need a functor to trigger it: once you know that two types
> are equal
> in some scope, there are many ways to leak that information.
>
> =============================
> Here comes the request for feedback:
>
> The fix is simple enough: we should track injectivity, and assume that
> abstract
> types are not injective by default.
> However, this also means that the first type I defined above (using
> Hashtbl.t)
> will be refused.
>
> How many of you are using GADTs in this way, and how dependent are you on
> abstract types ?
>
>
> If we need to be able to define GADTs relying on the injectivity of some
> abstract
> types, a straightforward solution is to add injectivity annotations on
> type parameters,
> the same way it was done for variance:
>
>   type #'a t
>
> would mean that t is injective.
> This is implemented in branches/non-vanishing, in the subversion
> repository.
>         svn checkout http://caml.inria.fr/svn/branches/non-vanishing
> Note that in that branch Hashtbl.t in the standard library is marked as
> injective.
> This introduces some new syntax, and libraries have to be modified to
> benefit,
> so the question is do we really need it?
>
>
> Jacques Garrigue
>
> P.S.
> If you are curious about other difficulties of GADTs, the is also
> branches/abstract-new
> which introduces a notion of new types, either concrete or abstract, which
> are guaranteed
> to be distinct from each other, and any other concrete type. This is
> useful when
> checking the exhaustiveness of pattern-matching, as we can then discard
> impossible
> branches. That branch is completely experimental.
>
>
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

[-- Attachment #2: Type: text/html, Size: 5933 bytes --]

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-28  2:45 ` Markus Mottl
@ 2013-04-28 10:28   ` Jacques Garrigue
  0 siblings, 0 replies; 37+ messages in thread
From: Jacques Garrigue @ 2013-04-28 10:28 UTC (permalink / raw)
  To: Markus Mottl; +Cc: OCaML List Mailing

On 2013/04/28, at 11:45, Markus Mottl <markus.mottl@gmail.com> wrote:

> On Sat, Apr 27, 2013 at 8:02 PM, Jacques Garrigue
> <garrigue@math.nagoya-u.ac.jp> wrote:
>> The fix is simple enough: we should track injectivity, and assume that abstract
>> types are not injective by default.
>> However, this also means that the first type I defined above (using Hashtbl.t)
>> will be refused.
> 
> If I understand your proposal correctly, the first type would only be
> refused unless the standard Hashtbl module were updated with
> appropriate injectivity annotations.  The type variables in the GADT
> would not need any annotations, because it is assumed that they have
> to be injective there anyway for soundness.

Yes, only abstract types need to be annotated.
Injectivity only makes sense for type aliases and abstract types (and private row types,
which hide an abstract type), since concrete types are always injective. For instance,
   type 'a t = T of int
is injective (you cannot unify  int t with bool t).

>> How many of you are using GADTs in this way, and how dependent are you on
>> abstract types ?
> 
> This seems like a "one percenter" problem.  OTOH, I guess most people
> also rarely run into problems where variance annotations matter, but
> if they do, they have no option.  The question should therefore rather
> be: what's the alternative?  Are there workarounds?  If not,
> annotations may just be a necessity to support advanced use cases.

At this point I think very few people are concerned.
So it might be the case that we could delay injectivity annotations until 4.02.
However, there is no easy workaround for abstract types.
The simplest one I can think of is duplicating types:

   type ('a,'b) t_impl
   type ('a, 'b) = Hashtbl of ('a,'b) t_impl

>> If we need to be able to define GADTs relying on the injectivity of some abstract
>> types, a straightforward solution is to add injectivity annotations on type parameters,
>> the same way it was done for variance:
>> 
>>  type #'a t
>> 
>> would mean that t is injective.
> 
> Seems concise, but I'm just a little worried that we may be raising
> the bar for beginners again.  The standard library would have to be
> updated with another annotation that may seem as obscure to beginners
> as variance annotations.  I hope we'll never have to explain to a
> beginner the meaning of $%^&#-'a.  Maybe a more verbose type
> constraint language would have been the better design choice here, but
> I guess we've already left this path with variance annotations.

As Jacques Le Normand pointed, injectivity is a property of parameters,
not of the whole type. (But in most cases a type is injective in all its parameters).

The addition of hard to read information is indeed the main drawback.
The fortunate part here is that, as long as you don't use GADTs, you need not
understand the meaning.

>> If you are curious about other difficulties of GADTs, the is also branches/abstract-new
>> which introduces a notion of new types, either concrete or abstract, which are guaranteed
>> to be distinct from each other, and any other concrete type. This is useful when
>> checking the exhaustiveness of pattern-matching, as we can then discard impossible
>> branches. That branch is completely experimental.
> 
> I haven't looked at this feature yet, but would this require even more
> annotations in the standard library?  If so, maybe it would indeed be
> time to think about a more accessible syntax for type constraints.

This is not really a replacement, since the requirements to be "new" are rather
restrictive. However, as a new type is automatically injective in all its parameters,
you would never need to make its injectivity explicit.
But we need indeed to think about the syntax.
All the more as other features, like the addition of runtime type information,
may also require annotations on types.

	Jacques


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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-28  0:02 [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
  2013-04-28  2:45 ` Markus Mottl
  2013-04-28  5:54 ` Jacques Le Normand
@ 2013-04-29  3:45 ` Ivan Gotovchits
  2013-04-29  4:03   ` Ivan Gotovchits
  2013-04-29  5:17 ` Jacques Le Normand
  2013-07-01 14:47 ` Alain Frisch
  4 siblings, 1 reply; 37+ messages in thread
From: Ivan Gotovchits @ 2013-04-29  3:45 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

Jacques Garrigue <garrigue@math.nagoya-u.ac.jp> writes:
>
> If we need to be able to define GADTs relying on the injectivity of some abstract
> types, a straightforward solution is to add injectivity annotations on type parameters,
> the same way it was done for variance:
>
>   type #'a t
>

I'm not sure that symbol '#' is a good choice. It is already used for
open types and may impose difficulties in undestanding code (and
compiler messages) when overriden. For me '#' is something polymorphic
and related to OOP. 

And indeed, «#'a» is to perlish... Personally, I would prefer something
like:

  type 'a t
  constraint 'a is injective


P.S. the following symbols looks more «injective» for me: '>', '^', '=',
though the last one can be confused with the invariance.



-- 
         (__) 
         (oo) 
   /------\/ 
  / |    ||   
 *  /\---/\ 
    ~~   ~~   
...."Have you mooed today?"...

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29  3:45 ` Ivan Gotovchits
@ 2013-04-29  4:03   ` Ivan Gotovchits
  0 siblings, 0 replies; 37+ messages in thread
From: Ivan Gotovchits @ 2013-04-29  4:03 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

Ivan Gotovchits <ivg@ieee.org> writes:


> I'm not sure that symbol '#' is a good choice. It is already used for
> open types and may impose difficulties in undestanding code (and
> compiler messages) when overriden. For me '#' is something polymorphic
> and related to OOP. 
>
> And indeed, «#'a» is to perlish... Personally, I would prefer something
> like:
>
>   type 'a t
>   constraint 'a is injective
>
>
> P.S. the following symbols looks more «injective» for me: '>', '^', '=',
> though the last one can be confused with the invariance.

P.P.S. Here is one more: '~' as it stands for equivalence.

-- 
         (__) 
         (oo) 
   /------\/ 
  / |    ||   
 *  /\---/\ 
    ~~   ~~   
...."Have you mooed today?"...

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-28  0:02 [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
                   ` (2 preceding siblings ...)
  2013-04-29  3:45 ` Ivan Gotovchits
@ 2013-04-29  5:17 ` Jacques Le Normand
  2013-04-29  7:58   ` Alain Frisch
  2013-04-29  7:59   ` Gabriel Scherer
  2013-07-01 14:47 ` Alain Frisch
  4 siblings, 2 replies; 37+ messages in thread
From: Jacques Le Normand @ 2013-04-29  5:17 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

[-- Attachment #1: Type: text/plain, Size: 4871 bytes --]

Fully injective types are the norm. It would have been nice to have a "non
injectivity" or "phantom type" annotation. Since phantom types are seldom
used, beginners won't get confused. It might even help beginners understand
the concept of a phantom type.



On Sat, Apr 27, 2013 at 5:02 PM, Jacques Garrigue <
garrigue@math.nagoya-u.ac.jp> wrote:

> Some of you may already be aware of PR#5985
> http://caml.inria.fr/mantis/view.php?id=5985
>
> To explain very shortly what is happening, a bug was found in the ocaml
> compiler, both 4.00 and the development version, such that one that
> convert a value from any type to any other type, bypassing all checks.
> This is what we call a soundness bug, a hole in the type system.
>
> Such problems happen at times, and we are usually very fast to fix them.
> However this time the problem is bit more subtle, because fixing it
> straightforwardly may make impossible to write a whole category of
> GADTs. Hence this request for feedback.
>
> Practical description of the problem:
>
> It is quite usual to write type witnesses using a GADT:
>
> type _ ty =
>   | Int : int ty
>   | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>   | Hashtbl : 'a ty * 'b ty -> ('a, 'b) Hashtbl.t ty
>
> This looks fine, but now suppose that we write the following code:
>
> module F (X : sig type 'a t end) = struct
>    type _ ty =
>     | Int : int ty
>     | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>     | T : 'a ty -> 'a X.t ty
> end
>
> The idea is to create a type witness with the
> type constructor X.t instead of Hashtbl.t.
> Now I can use it this way:
>
> module U = struct type 'a t = unit end
> module M = F(U)
> let w = M.T M.Int
> (* val w : int U.t M.ty *)
> let w' = match w with M.T x -> x | _ -> assert false
> (* val w' : '_a M.ty = M.Int *)
>
> What this means is that we can now give an arbitrary type
> to a witness for int. You can easily use it to build a function
> from int to any  type:
>
> let f : type a. a M.ty -> int -> a =
>   fun w x -> match w with M.Int -> x | _ -> assert false;;
> let g : int -> float = f w';;
>
> If we look at the source of the problem, it lies in the fact U.t is not
> injective.
> I.e. when we define a GADT, we assume that all the type variables in
> the return type can be determined from the type parameter.
> That is, from (int U.t M.ty) we assume that the type of the argument of T
> is (int M.ty).
> However, since U.t is not injective, int U.t is actually equal to any type
> 'a U.t,
> and we can convert.
> Note that, for known types, injectivity was already checked.
> I.e., the following definition is not accepted:
>
> type _ ty =
>   | Int : int ty
>   | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>   | T : 'a ty -> 'a U.t ty
>
> However, abstract types were assumed to be injective.
> Here we see that you cannot take this for granted.
>
> The problem is of course not limited to type witnesses, and not even GADTs:
> types with constrained parameters (introduced at the very beginning of
> ocaml),
> can also trigger it.
> And you don't need a functor to trigger it: once you know that two types
> are equal
> in some scope, there are many ways to leak that information.
>
> =============================
> Here comes the request for feedback:
>
> The fix is simple enough: we should track injectivity, and assume that
> abstract
> types are not injective by default.
> However, this also means that the first type I defined above (using
> Hashtbl.t)
> will be refused.
>
> How many of you are using GADTs in this way, and how dependent are you on
> abstract types ?
>
>
> If we need to be able to define GADTs relying on the injectivity of some
> abstract
> types, a straightforward solution is to add injectivity annotations on
> type parameters,
> the same way it was done for variance:
>
>   type #'a t
>
> would mean that t is injective.
> This is implemented in branches/non-vanishing, in the subversion
> repository.
>         svn checkout http://caml.inria.fr/svn/branches/non-vanishing
> Note that in that branch Hashtbl.t in the standard library is marked as
> injective.
> This introduces some new syntax, and libraries have to be modified to
> benefit,
> so the question is do we really need it?
>
>
> Jacques Garrigue
>
> P.S.
> If you are curious about other difficulties of GADTs, the is also
> branches/abstract-new
> which introduces a notion of new types, either concrete or abstract, which
> are guaranteed
> to be distinct from each other, and any other concrete type. This is
> useful when
> checking the exhaustiveness of pattern-matching, as we can then discard
> impossible
> branches. That branch is completely experimental.
>
>
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

[-- Attachment #2: Type: text/html, Size: 6145 bytes --]

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29  5:17 ` Jacques Le Normand
@ 2013-04-29  7:58   ` Alain Frisch
  2013-04-29 10:52     ` Jacques Garrigue
  2013-04-29  7:59   ` Gabriel Scherer
  1 sibling, 1 reply; 37+ messages in thread
From: Alain Frisch @ 2013-04-29  7:58 UTC (permalink / raw)
  To: Jacques Le Normand, Jacques Garrigue; +Cc: OCaML List Mailing

On 04/29/2013 07:17 AM, Jacques Le Normand wrote:
> Fully injective types are the norm. It would have been nice to have a
> "non injectivity" or "phantom type" annotation. Since phantom types are
> seldom used, beginners won't get confused. It might even help beginners
> understand the concept of a phantom type.

I'd also prefer an annotation on "non injective" type parameters, rather 
than on "injective" one.  The problem with this approach is that it 
requires existing well-typed code to be rewritten, to add these 
annotations (while annotating injectivity will only impact code using 
GADTs).  I still believe this is less intrusive than requiring most 
parametrized abstract type to be annotated, or else making GADTs less 
useful.

For the transition, we could have two compilation modes (decided on the 
command-line) for this feature.  In "non-injective" (legacy) mode, every 
parametrized abstract type would be assumed to be non-injective.  This 
would allow to compile existing code bases (at least those not relying 
on GADTs).  In "injective-by-default" mode, non-injective parameters 
would need to be marked as such.  Enabling this mode will require a few 
annotations to be added to existing code, but this will be very easy, so 
I guess we don't need to support "injectivity" annotations the 
"non-injective" mode.


Alain

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29  5:17 ` Jacques Le Normand
  2013-04-29  7:58   ` Alain Frisch
@ 2013-04-29  7:59   ` Gabriel Scherer
  1 sibling, 0 replies; 37+ messages in thread
From: Gabriel Scherer @ 2013-04-29  7:59 UTC (permalink / raw)
  To: Jacques Le Normand; +Cc: Jacques Garrigue, OCaML List Mailing

On Mon, Apr 29, 2013 at 7:17 AM, Jacques Le Normand
<rathereasy@gmail.com> wrote:
> Fully injective types are the norm. It would have been nice to have a "non
> injectivity" or "phantom type" annotation. Since phantom types are seldom
> used, beginners won't get confused. It might even help beginners understand
> the concept of a phantom type.

In practice most definitions using what we call a "phantom type" are
in fact injective (because sum and record types are injective in all
parameters if your notion of type equality is unification), so it
would be more dangerous than helpful to link the two concepts.

You are right however to remark that in a sense the "right default" is
to be injective rather than be potentially non-injective. If we were
starting from void, it would make sense to pick (type 'a t) as meaning
that 'a is injective, and a different syntax for maybe-non-injective¹
types (this was suggested by other people in the discussion). However,
this would break backward compatibility as code that is working now,
providing a non-injective type to a signature (type 'a t), would then
fail to type-check.

There are two different design principles in tension here:
- the least path of resistance: the shortest syntax should have the
most common and useful meaning, and it is suboptimal to add a
complication that would apply in almost all cases
- pay (only) for what you use: if you don't use GADTs, you most
probably don't care about injectivity, so it would be wrong to break
existing code to facilitate a feature that authors of said code do not
use

I tend to think that having an explicit syntax for
potentially-non-injective types would be the best choice starting from
scratch (users will be surprised if their non-injective type fail to
satisfy a signature, but the error message can easily guide them
towards the right, local fix). But breaking compatibility is generally
not considered an option when evolving the OCaml language.

¹: there is a difference between "I guarantee you that this type is
not injective in this parameter" and "You cannot assume that this type
is injective in this parameter", and the latter is "more abstract"
than the former; it is the better notion for an abstract type about
which we know nothing at all (so that any type declaration may be an
instance of it). The former, stronger notion seems less useful, and so
far nobody has suggested to allow to express it.


> On Sat, Apr 27, 2013 at 5:02 PM, Jacques Garrigue
> <garrigue@math.nagoya-u.ac.jp> wrote:
>>
>> Some of you may already be aware of PR#5985
>> http://caml.inria.fr/mantis/view.php?id=5985
>>
>> To explain very shortly what is happening, a bug was found in the ocaml
>> compiler, both 4.00 and the development version, such that one that
>> convert a value from any type to any other type, bypassing all checks.
>> This is what we call a soundness bug, a hole in the type system.
>>
>> Such problems happen at times, and we are usually very fast to fix them.
>> However this time the problem is bit more subtle, because fixing it
>> straightforwardly may make impossible to write a whole category of
>> GADTs. Hence this request for feedback.
>>
>> Practical description of the problem:
>>
>> It is quite usual to write type witnesses using a GADT:
>>
>> type _ ty =
>>   | Int : int ty
>>   | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>>   | Hashtbl : 'a ty * 'b ty -> ('a, 'b) Hashtbl.t ty
>>
>> This looks fine, but now suppose that we write the following code:
>>
>> module F (X : sig type 'a t end) = struct
>>    type _ ty =
>>     | Int : int ty
>>     | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>>     | T : 'a ty -> 'a X.t ty
>> end
>>
>> The idea is to create a type witness with the
>> type constructor X.t instead of Hashtbl.t.
>> Now I can use it this way:
>>
>> module U = struct type 'a t = unit end
>> module M = F(U)
>> let w = M.T M.Int
>> (* val w : int U.t M.ty *)
>> let w' = match w with M.T x -> x | _ -> assert false
>> (* val w' : '_a M.ty = M.Int *)
>>
>> What this means is that we can now give an arbitrary type
>> to a witness for int. You can easily use it to build a function
>> from int to any  type:
>>
>> let f : type a. a M.ty -> int -> a =
>>   fun w x -> match w with M.Int -> x | _ -> assert false;;
>> let g : int -> float = f w';;
>>
>> If we look at the source of the problem, it lies in the fact U.t is not
>> injective.
>> I.e. when we define a GADT, we assume that all the type variables in
>> the return type can be determined from the type parameter.
>> That is, from (int U.t M.ty) we assume that the type of the argument of T
>> is (int M.ty).
>> However, since U.t is not injective, int U.t is actually equal to any type
>> 'a U.t,
>> and we can convert.
>> Note that, for known types, injectivity was already checked.
>> I.e., the following definition is not accepted:
>>
>> type _ ty =
>>   | Int : int ty
>>   | Pair : 'a ty * 'b ty -> ('a * 'b) ty
>>   | T : 'a ty -> 'a U.t ty
>>
>> However, abstract types were assumed to be injective.
>> Here we see that you cannot take this for granted.
>>
>> The problem is of course not limited to type witnesses, and not even
>> GADTs:
>> types with constrained parameters (introduced at the very beginning of
>> ocaml),
>> can also trigger it.
>> And you don't need a functor to trigger it: once you know that two types
>> are equal
>> in some scope, there are many ways to leak that information.
>>
>> =============================
>> Here comes the request for feedback:
>>
>> The fix is simple enough: we should track injectivity, and assume that
>> abstract
>> types are not injective by default.
>> However, this also means that the first type I defined above (using
>> Hashtbl.t)
>> will be refused.
>>
>> How many of you are using GADTs in this way, and how dependent are you on
>> abstract types ?
>>
>>
>> If we need to be able to define GADTs relying on the injectivity of some
>> abstract
>> types, a straightforward solution is to add injectivity annotations on
>> type parameters,
>> the same way it was done for variance:
>>
>>   type #'a t
>>
>> would mean that t is injective.
>> This is implemented in branches/non-vanishing, in the subversion
>> repository.
>>         svn checkout http://caml.inria.fr/svn/branches/non-vanishing
>> Note that in that branch Hashtbl.t in the standard library is marked as
>> injective.
>> This introduces some new syntax, and libraries have to be modified to
>> benefit,
>> so the question is do we really need it?
>>
>>
>> Jacques Garrigue
>>
>> P.S.
>> If you are curious about other difficulties of GADTs, the is also
>> branches/abstract-new
>> which introduces a notion of new types, either concrete or abstract, which
>> are guaranteed
>> to be distinct from each other, and any other concrete type. This is
>> useful when
>> checking the exhaustiveness of pattern-matching, as we can then discard
>> impossible
>> branches. That branch is completely experimental.
>>
>>
>> --
>> Caml-list mailing list.  Subscription management and archives:
>> https://sympa.inria.fr/sympa/arc/caml-list
>> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
>> Bug reports: http://caml.inria.fr/bin/caml-bugs
>
>

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29  7:58   ` Alain Frisch
@ 2013-04-29 10:52     ` Jacques Garrigue
  2013-04-29 11:23       ` Alain Frisch
                         ` (2 more replies)
  0 siblings, 3 replies; 37+ messages in thread
From: Jacques Garrigue @ 2013-04-29 10:52 UTC (permalink / raw)
  To: OCaML List Mailing

Dear Camlers,

First, let me reiterate my request for feedback:

What I want to know is whether anybody is using GADTs in a way that
would be broken if we disallow type variables under abstract types
(other  than the predefined ones) in the return type of GADTs.
I.e., for instance defining a type witness type involving such abstract types.

This is really the question I want you all to answer.

If this is not the case, we can safely prohibit that at this point, and take
our time to think about the solution.

But if there are some users, we had better provide at least some mechanism,
and injectivity annotations seem to be the less intrusive (they don't
break any code, at least any existing code).
We may need to make clear that they are experimental and might
disappear, but at least we are not in a complete void.

Now just my 2 centimes on alternative approaches

On 2013/04/29, at 16:58, Alain Frisch <alain@frisch.fr> wrote:

> On 04/29/2013 07:17 AM, Jacques Le Normand wrote:
>> Fully injective types are the norm. It would have been nice to have a
>> "non injectivity" or "phantom type" annotation. Since phantom types are
>> seldom used, beginners won't get confused. It might even help beginners
>> understand the concept of a phantom type.
> 
> I'd also prefer an annotation on "non injective" type parameters, rather than on "injective" one.  The problem with this approach is that it requires existing well-typed code to be rewritten, to add these annotations (while annotating injectivity will only impact code using GADTs).  I still believe this is less intrusive than requiring most parametrized abstract type to be annotated, or else making GADTs less useful.

This may be a good idea for 5.00, but honestly this is a big change.

Moreover it is not so clear that this is the right choice for functors:
when a functor takes an abstract type as argument, it usually doesn't care
whether it is injective or not. And in practice there is a technique of adding
a type parameter to abstract types just in case we want to pass a parameterized
type, but usually using this functor for non-parameterized types.

Another point is that I'm not sure how much "less useful" GADTs become when one
forgets an injectivity annotation somewhere.

> For the transition, we could have two compilation modes (decided on the command-line) for this feature.  In "non-injective" (legacy) mode, every parametrized abstract type would be assumed to be non-injective.  This would allow to compile existing code bases (at least those not relying on GADTs).  In "injective-by-default" mode, non-injective parameters would need to be marked as such.  Enabling this mode will require a few annotations to be added to existing code, but this will be very easy, so I guess we don't need to support "injectivity" annotations the "non-injective" mode.

Again, the defect of such a mode is that it is going to apply to everything, including functors.
A functor compiled in this mode might be not be applicable to some modules, whereas there was
no reason from the beginning to require injectivity there.

And just using variance to change the behavior is not going to work well:
a standard practice is to explicitly define module types for the input and output of
the functor. We want the output types to be injective, but we don't really need such
requirement for the input types. But they are just module type definitions…
(See hashtbl.mli for instance for this pattern.)

Jacques

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29 10:52     ` Jacques Garrigue
@ 2013-04-29 11:23       ` Alain Frisch
  2013-04-29 16:37         ` Nathan Mishra Linger
  2013-04-30  5:45       ` Jacques Garrigue
  2013-04-30  6:59       ` Alain Frisch
  2 siblings, 1 reply; 37+ messages in thread
From: Alain Frisch @ 2013-04-29 11:23 UTC (permalink / raw)
  To: Jacques Garrigue, OCaML List Mailing

On 04/29/2013 12:52 PM, Jacques Garrigue wrote:
> What I want to know is whether anybody is using GADTs in a way that
> would be broken if we disallow type variables under abstract types
> (other  than the predefined ones) in the return type of GADTs.
> I.e., for instance defining a type witness type involving such abstract types.

The only abstract types in the return type of GADTs in LexiFi's code 
base code are currently either non-parametrized ones or built-in types 
(array, lazy).  So we'd be fine with the proposed fix (assuming that 
abstract types are not injective).

-- Alain

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29 11:23       ` Alain Frisch
@ 2013-04-29 16:37         ` Nathan Mishra Linger
  2013-04-29 23:53           ` Jacques Garrigue
  0 siblings, 1 reply; 37+ messages in thread
From: Nathan Mishra Linger @ 2013-04-29 16:37 UTC (permalink / raw)
  To: Alain Frisch; +Cc: Jacques Garrigue, OCaML List Mailing

Our codebase at Jane Street would not suffer from the proposed fix.

In fact, we have previously wished the type system tracked injectivity of type
constructors.  Because it didn't, we wrote Type_equal.Injective [^1] for cases
when we need to track injectivity ourselves.

[^1] https://ocaml.janestreet.com/ocaml-core/latest/doc/core/Type_equal.Injective.html

On Mon, Apr 29, 2013 at 7:23 AM, Alain Frisch <alain@frisch.fr> wrote:
> On 04/29/2013 12:52 PM, Jacques Garrigue wrote:
>>
>> What I want to know is whether anybody is using GADTs in a way that
>> would be broken if we disallow type variables under abstract types
>> (other  than the predefined ones) in the return type of GADTs.
>> I.e., for instance defining a type witness type involving such abstract
>> types.
>
>
> The only abstract types in the return type of GADTs in LexiFi's code base
> code are currently either non-parametrized ones or built-in types (array,
> lazy).  So we'd be fine with the proposed fix (assuming that abstract types
> are not injective).
>
> -- Alain
>
>
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29 16:37         ` Nathan Mishra Linger
@ 2013-04-29 23:53           ` Jacques Garrigue
  0 siblings, 0 replies; 37+ messages in thread
From: Jacques Garrigue @ 2013-04-29 23:53 UTC (permalink / raw)
  To: Nathan Mishra Linger; +Cc: OCaML List Mailing

On 2013/04/30, at 1:37, Nathan Mishra Linger <nathan.mishralinger@gmail.com> wrote:

> Our codebase at Jane Street would not suffer from the proposed fix.
> 
> In fact, we have previously wished the type system tracked injectivity of type
> constructors.  Because it didn't, we wrote Type_equal.Injective [^1] for cases
> when we need to track injectivity ourselves.
> 
> [^1] https://ocaml.janestreet.com/ocaml-core/latest/doc/core/Type_equal.Injective.html

Thank you for the pointer.

One could say this actually shows the need for injectivity annotations
(eventhough Core currently works around them).

To be more precise, injectivity has several uses:

1) To allow you to infer equations between types.
   For instance, assuming the standard equality witness type:

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

   the following function typeckecks

        let conv1 : type a b. (a array, b array) equal -> a -> b = fun Eq x -> x

   because array is known to be injective, but  this one doesn't

	let conv2 : type a b. (a Queue.t, b Queue.t) equal -> a -> b = fun Eq x -> x

   because Queue.t is an abstract type whose injectivity is unknown.

   This part was correctly handled since 4.00.0.

2) To allow you to prove exhaustiveness of pattern matching, by ensuring
   that other cases cannot happen. For instance,

	type (_,_) comp =
	  | Ceq : ('a,'a) comp
	  | Cany : ('a,'b) comp

	let f (x : (int array,bool array) comp) = match x with Cany -> ()
       
   causes no warning, because the type system can prove that Ceq since
   int array and bool array cannot be equal in any scope. However

	let f (x : (int Queue.t, bool Queue.t) comp) = match x with Cany -> 0

   should emit a warning, because we cannot prove that int Queue.t and
   bool Queue.t are distinct in the absence of injectivity information.
   Note that this is not correctly handled in 4.00, and you will get no warning
   in this situation. Worse, since the code is optimized accordingly (i.e. no
   runtime check is introduced), if the type is really not injective, and you
   pass Eq, you can use this as a hole in the type system.
   This now works correctly in trunk (PR#5981).

3) The problem I describe in my first mail. I.e. when defining a type,
   if you use type variables appearing in constrained type parameters,
   you need the type constructors leading to the type variables to be
   injective. This is PR#5985, and it is only fixed in branches/non-vanishing.

The goal of the Type_equal.Injective interface in Core  is to work around the
limitation on abstract types in (1), by letting you prove injectivity by hand.
But if we had injectivity annotations, this would solve simultaneously all 3
problems: no need to build proofs by hand anymore.

So I understand you answer as: Jane Street is not directly concerned by
(3) at this point, and has already developed a workaround for (1).
I suppose you could profit from injectivity annotations, since you had
to develop a workaround, but this may not be that urgent.

(2) is not so much a problem, since it only means that you may have to
add extra cases to pattern-matching.

Jacques Garrigue

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29 10:52     ` Jacques Garrigue
  2013-04-29 11:23       ` Alain Frisch
@ 2013-04-30  5:45       ` Jacques Garrigue
  2013-05-04  6:46         ` Jacques Garrigue
  2013-04-30  6:59       ` Alain Frisch
  2 siblings, 1 reply; 37+ messages in thread
From: Jacques Garrigue @ 2013-04-30  5:45 UTC (permalink / raw)
  To: OCaML List Mailing

I have now committed in trunk a fix to PR#5985.
You can use it to test whether your codebase runs into problems.
You can either obtain it from subversion directly
	svn checkout http://caml.inria.fr/svn/ocaml/trunk
or use opam to do it for you.

I checked that at least Core itself compiles without problems.

Again, if you run into problems, you can try branches/non-vanishing,
which allows you to annotate some abstract types are injective.

Jacques Garrigue

On 2013/04/29, at 19:52, Jacques Garrigue <garrigue@math.nagoya-u.ac.jp> wrote:

> Dear Camlers,
> 
> First, let me reiterate my request for feedback:
> 
> What I want to know is whether anybody is using GADTs in a way that
> would be broken if we disallow type variables under abstract types
> (other  than the predefined ones) in the return type of GADTs.
> I.e., for instance defining a type witness type involving such abstract types.
> 
> This is really the question I want you all to answer.
> 
> If this is not the case, we can safely prohibit that at this point, and take
> our time to think about the solution.


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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-29 10:52     ` Jacques Garrigue
  2013-04-29 11:23       ` Alain Frisch
  2013-04-30  5:45       ` Jacques Garrigue
@ 2013-04-30  6:59       ` Alain Frisch
  2013-04-30  7:56         ` Jacques Garrigue
  2 siblings, 1 reply; 37+ messages in thread
From: Alain Frisch @ 2013-04-30  6:59 UTC (permalink / raw)
  To: Jacques Garrigue, OCaML List Mailing

On 04/29/2013 12:52 PM, Jacques Garrigue wrote:
> Again, the defect of such a mode is that it is going to apply to everything, including functors.
> A functor compiled in this mode might be not be applicable to some modules, whereas there was
> no reason from the beginning to require injectivity there.
>
> And just using variance to change the behavior is not going to work well:
> a standard practice is to explicitly define module types for the input and output of
> the functor. We want the output types to be injective, but we don't really need such
> requirement for the input types. But they are just module type definitions…
> (See hashtbl.mli for instance for this pattern.)

These are interesting arguments against the "injective by default" mode 
(explicit non-injectivity annotations).  I did not think about the 
consequences on functors indeed.  In practice, though, it might be ok to 
annotate, if needed, some module types used as functor arguments with 
non-injectivity annotations (or more precisely "not-necessarily 
injective" annotations).  But this certainly deserves some careful thinking.

It's reassuring to see that the conservative solution (not assuming 
injectivity of user defined abstract types) does not seem too bad for 
now, even if not very satisfying.


I'm only concerned with:

> 3) The problem I describe in my first mail. I.e. when defining a type,
>    if you use type variables appearing in constrained type parameters,
>    you need the type constructors leading to the type variables to be
>    injective. This is PR#5985, and it is only fixed in branches/non-vanishing.


Do you think that fixing this unsoundness (without injectivity 
annotations) would lead to reject existing programs?


-- Alain


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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30  6:59       ` Alain Frisch
@ 2013-04-30  7:56         ` Jacques Garrigue
  2013-04-30  8:02           ` Alain Frisch
  0 siblings, 1 reply; 37+ messages in thread
From: Jacques Garrigue @ 2013-04-30  7:56 UTC (permalink / raw)
  To: Alain Frisch; +Cc: OCaML List Mailing

On 2013/04/30, at 15:59, Alain Frisch <alain@frisch.fr> wrote:

> It's reassuring to see that the conservative solution (not assuming injectivity of user defined abstract types) does not seem too bad for now, even if not very satisfying.
> 
> I'm only concerned with:
> 
>> 3) The problem I describe in my first mail. I.e. when defining a type,
>>   if you use type variables appearing in constrained type parameters,
>>   you need the type constructors leading to the type variables to be
>>   injective. This is PR#5985, and it is only fixed in branches/non-vanishing.
> 
> Do you think that fixing this unsoundness (without injectivity annotations) would lead to reject existing programs?

Potentially yes.
But I don't know how it is in practice for code existing now.
Hence my question.

I have now committed the fix in trunk, so people can try it.

A reason the impact is going to be limited, is that usually when defining
a GADT for type witnesses, one only lists predefined types, and uses
another mechanism for user defined types, like what is done in Core.
If you use a mechanism based on equality witnesses, (lack of) injectivity
already came in the way before, so this is not a new phenomenon.

For other applications of GADTs, you are defining type indices yourself,
so you just have to be careful to use concrete types instead of abstract types.

Jacques Garrigue

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30  7:56         ` Jacques Garrigue
@ 2013-04-30  8:02           ` Alain Frisch
  2013-04-30  8:18             ` Jacques Garrigue
  0 siblings, 1 reply; 37+ messages in thread
From: Alain Frisch @ 2013-04-30  8:02 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

On 04/30/2013 09:56 AM, Jacques Garrigue wrote:
> On 2013/04/30, at 15:59, Alain Frisch <alain@frisch.fr> wrote:
>
>> It's reassuring to see that the conservative solution (not assuming injectivity of user defined abstract types) does not seem too bad for now, even if not very satisfying.
>>
>> I'm only concerned with:
>>
>>> 3) The problem I describe in my first mail. I.e. when defining a type,
>>>    if you use type variables appearing in constrained type parameters,
>>>    you need the type constructors leading to the type variables to be
>>>    injective. This is PR#5985, and it is only fixed in branches/non-vanishing.
>>
>> Do you think that fixing this unsoundness (without injectivity annotations) would lead to reject existing programs?
>
> Potentially yes.
> But I don't know how it is in practice for code existing now.
> Hence my question.

Apart from GADTs, does your patch address the unsoundness with type 
constraints?


Alain

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30  8:02           ` Alain Frisch
@ 2013-04-30  8:18             ` Jacques Garrigue
  2013-04-30  9:11               ` Gabriel Scherer
  0 siblings, 1 reply; 37+ messages in thread
From: Jacques Garrigue @ 2013-04-30  8:18 UTC (permalink / raw)
  To: Alain Frisch; +Cc: OCaML List Mailing

On 2013/04/30, at 17:02, Alain Frisch <alain@frisch.fr> wrote:

> On 04/30/2013 09:56 AM, Jacques Garrigue wrote:
>> On 2013/04/30, at 15:59, Alain Frisch <alain@frisch.fr> wrote:
>> 
>>> It's reassuring to see that the conservative solution (not assuming injectivity of user defined abstract types) does not seem too bad for now, even if not very satisfying.
>>> 
>>> I'm only concerned with:
>>> 
>>>> 3) The problem I describe in my first mail. I.e. when defining a type,
>>>>   if you use type variables appearing in constrained type parameters,
>>>>   you need the type constructors leading to the type variables to be
>>>>   injective. This is PR#5985, and it is only fixed in branches/non-vanishing.
>>> 
>>> Do you think that fixing this unsoundness (without injectivity annotations) would lead to reject existing programs?
>> 
>> Potentially yes.
>> But I don't know how it is in practice for code existing now.
>> Hence my question.
> 
> Apart from GADTs, does your patch address the unsoundness with type constraints?

Sure, this is exactly the same mechanism.

The only other thing it does is a slight strengthening of variance checking.

Consider the type
   type 'a t = T                   (* 'a bi-variant and injective *)
   type 'a u = 'a t -> 'a t    (* 'a t occurs both at positive and negative positions *)

Originally, the parameter of u would have been bi-variant (or unrestricted)
since it is bi-variant in the definition of t.
However it is now invariant.
The reason is that you can only change it by subtyping in t, and u doesn't allow subtyping.
This is a reasonable restriction, and it is necessary to allow some GADT
definitions where we use concrete types as indices.

Jacques Garrigue

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30  8:18             ` Jacques Garrigue
@ 2013-04-30  9:11               ` Gabriel Scherer
  2013-04-30  9:55                 ` Jacques Garrigue
  0 siblings, 1 reply; 37+ messages in thread
From: Gabriel Scherer @ 2013-04-30  9:11 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: Alain Frisch, OCaML List Mailing

> The only other thing it does is a slight strengthening of variance checking.
>
> Consider the type
>    type 'a t = T                   (* 'a bi-variant and injective *)
>    type 'a u = 'a t -> 'a t    (* 'a t occurs both at positive and negative positions *)
>
> Originally, the parameter of u would have been bi-variant (or unrestricted)
> since it is bi-variant in the definition of t.
> However it is now invariant.
> The reason is that you can only change it by subtyping in t, and u doesn't allow subtyping.
> This is a reasonable restriction, and it is necessary to allow some GADT
> definitions where we use concrete types as indices.

I'm not sure about this. In our work on variance of GADTs (
http://arxiv.org/abs/1301.2903 ), we defined equality exactly as the
antisymmetric closure of the subtyping relation (as is done in the
previous work by Simonet and Pottier), and all type constructors are
functional: (a = b) implies (a t = b t). This means that in our
formalization, you ('a u = 'a bivar -> 'a bivar) is bivariant, because
('a bivar = 'b bivar) for any 'a and 'b implies (a u = 'a bivar -> 'a
bivar = 'b bivar -> 'b bivar = 'b u).

This vision of invariance as still functional also plays nicely with
the inversion principle: when you have (a t <= b t) when t covariant,
you can deduce (a <= b), when t is contravariant you have (a >= b),
and we can explain invariance as saying that you then have both, (a <=
b) and (b <= a), which coincides with the algorithmic notion of
"occurs both negatively and positively". The nice thing is that this
inversion criterion is also complete, from (a <= b) and (b <= a) you
can deduce (a t <= b t) for t invariant (in our system).

What is the reason for adding your strengthening? What I understood so
far is that unification, and therefore provable equality/inequalities,
were orthogonal to variance (eg. (type 'a t = T) is both bivariant and
injective). Is there a reason to tie them back together precisely in
the invariant case?

On Tue, Apr 30, 2013 at 10:18 AM, Jacques Garrigue
<garrigue@math.nagoya-u.ac.jp> wrote:
> On 2013/04/30, at 17:02, Alain Frisch <alain@frisch.fr> wrote:
>
>> On 04/30/2013 09:56 AM, Jacques Garrigue wrote:
>>> On 2013/04/30, at 15:59, Alain Frisch <alain@frisch.fr> wrote:
>>>
>>>> It's reassuring to see that the conservative solution (not assuming injectivity of user defined abstract types) does not seem too bad for now, even if not very satisfying.
>>>>
>>>> I'm only concerned with:
>>>>
>>>>> 3) The problem I describe in my first mail. I.e. when defining a type,
>>>>>   if you use type variables appearing in constrained type parameters,
>>>>>   you need the type constructors leading to the type variables to be
>>>>>   injective. This is PR#5985, and it is only fixed in branches/non-vanishing.
>>>>
>>>> Do you think that fixing this unsoundness (without injectivity annotations) would lead to reject existing programs?
>>>
>>> Potentially yes.
>>> But I don't know how it is in practice for code existing now.
>>> Hence my question.
>>
>> Apart from GADTs, does your patch address the unsoundness with type constraints?
>
> Sure, this is exactly the same mechanism.
>
> The only other thing it does is a slight strengthening of variance checking.
>
> Consider the type
>    type 'a t = T                   (* 'a bi-variant and injective *)
>    type 'a u = 'a t -> 'a t    (* 'a t occurs both at positive and negative positions *)
>
> Originally, the parameter of u would have been bi-variant (or unrestricted)
> since it is bi-variant in the definition of t.
> However it is now invariant.
> The reason is that you can only change it by subtyping in t, and u doesn't allow subtyping.
> This is a reasonable restriction, and it is necessary to allow some GADT
> definitions where we use concrete types as indices.
>
> Jacques Garrigue
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30  9:11               ` Gabriel Scherer
@ 2013-04-30  9:55                 ` Jacques Garrigue
  2013-04-30 10:12                   ` Leo White
  0 siblings, 1 reply; 37+ messages in thread
From: Jacques Garrigue @ 2013-04-30  9:55 UTC (permalink / raw)
  To: Gabriel Scherer; +Cc: OCaML List Mailing

On 2013/04/30, at 18:11, Gabriel Scherer <gabriel.scherer@gmail.com> wrote:

>> The only other thing it does is a slight strengthening of variance checking.
>> 
>> Consider the type
>>   type 'a t = T                   (* 'a bi-variant and injective *)
>>   type 'a u = 'a t -> 'a t    (* 'a t occurs both at positive and negative positions *)
>> 
>> Originally, the parameter of u would have been bi-variant (or unrestricted)
>> since it is bi-variant in the definition of t.
>> However it is now invariant.
>> The reason is that you can only change it by subtyping in t, and u doesn't allow subtyping.
>> This is a reasonable restriction, and it is necessary to allow some GADT
>> definitions where we use concrete types as indices.
> 
> I'm not sure about this. In our work on variance of GADTs (
> http://arxiv.org/abs/1301.2903 ), we defined equality exactly as the
> antisymmetric closure of the subtyping relation (as is done in the
> previous work by Simonet and Pottier), and all type constructors are
> functional: (a = b) implies (a t = b t). This means that in our
> formalization, you ('a u = 'a bivar -> 'a bivar) is bivariant, because
> ('a bivar = 'b bivar) for any 'a and 'b implies (a u = 'a bivar -> 'a
> bivar = 'b bivar -> 'b bivar = 'b u).
> 
> This vision of invariance as still functional also plays nicely with
> the inversion principle: when you have (a t <= b t) when t covariant,
> you can deduce (a <= b), when t is contravariant you have (a >= b),
> and we can explain invariance as saying that you then have both, (a <=
> b) and (b <= a), which coincides with the algorithmic notion of
> "occurs both negatively and positively". The nice thing is that this
> inversion criterion is also complete, from (a <= b) and (b <= a) you
> can deduce (a t <= b t) for t invariant (in our system).

But it seems to me that this contradicts the definition of injectivity.
Namely, if we follow your definition, and have 'a bivar = 'b bivar, then
clearly bivar is not injective.
So there are two solutions: either we do not allow a bi-variant type
to be injective (breaking our simple statement that concrete types
are injective in all their parameters), or we consider bi-variance +
injectivity is some intermediary state, where we can use both directions
of subtyping, but not strong (unification) equality.

> What is the reason for adding your strengthening? What I understood so
> far is that unification, and therefore provable equality/inequalities,
> were orthogonal to variance (eg. (type 'a t = T) is both bivariant and
> injective). Is there a reason to tie them back together precisely in
> the invariant case?


The theoretical reason is above.
The practical reason is to make easier to define indices.
If we keep the bi-variance in an invariant context, then the following
type definition is refused:

	type 'a t = T;;
	type _ g = G : 'a -> 'a t g;;

In 4.00, this definition is refused because 'a in 'a t g is bi-variant, but 'a appears
in a covariant position.

Jacques Garrigue

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30  9:55                 ` Jacques Garrigue
@ 2013-04-30 10:12                   ` Leo White
  2013-04-30 11:30                     ` Gabriel Scherer
  0 siblings, 1 reply; 37+ messages in thread
From: Leo White @ 2013-04-30 10:12 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: Gabriel Scherer, OCaML List Mailing

[Sorry if this is a repeat post]

> But it seems to me that this contradicts the definition of injectivity.
> Namely, if we follow your definition, and have 'a bivar = 'b bivar, then
> clearly bivar is not injective.

I think Gabriel meant that 'a bivar = 'b bivar were equal in the
subtyiping relationship, rather than in the unification
relationship. (Perhaps we should use a different symbol for subtyping
equality).

> So there are two solutions: either we do not allow a bi-variant type
> to be injective (breaking our simple statement that concrete types
> are injective in all their parameters), or we consider bi-variance +
> injectivity is some intermediary state, where we can use both directions
> of subtyping, but not strong (unification) equality.

I don't see how this implies the need for the strengthening you
describe. As I see it:

  type 'a t = T

creates a type that is bi-variant in its parameter, so all occurences of
'a in:

  type 'a u = 'a t -> 'a t

are in bi-variant positions, so u should also be bi-variant.

> The practical reason is to make easier to define indices.
> If we keep the bi-variance in an invariant context, then the following
> type definition is refused:
>
> 	type 'a t = T;;
> 	type _ g = G : 'a -> 'a t g;;
>
> In 4.00, this definition is refused because 'a in 'a t g is bi-variant, but 'a appears
> in a covariant position.

I don't see why this could not be allowed without the restriction you
propose. I thought that this was rejected in 4.00 because 4.00 used
bi-variance as an (unsafe) approximation of non-injective. Since we now
track injectivity separately from variance g be accepted (with 'a covariant).

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30 10:12                   ` Leo White
@ 2013-04-30 11:30                     ` Gabriel Scherer
  2013-04-30 13:06                       ` Leo White
  0 siblings, 1 reply; 37+ messages in thread
From: Gabriel Scherer @ 2013-04-30 11:30 UTC (permalink / raw)
  To: Leo White; +Cc: Jacques Garrigue, OCaML List Mailing

Indeed, the (=) in my message above where in the context of the work
where it is define as ((<=) and (>=)), so in a sense "equality in the
sense of subtyping rather than unification". Sorry for the confusion.
I'll now mark this relation as <=>.

>> The practical reason is to make easier to define indices.
>> If we keep the bi-variance in an invariant context, then the following
>> type definition is refused:
>>
>>       type 'a t = T;;
>>       type _ g = G : 'a -> 'a t g;;
>>
>> In 4.00, this definition is refused because 'a in 'a t g is bi-variant, but 'a appears
>> in a covariant position.
>
> I don't see why this could not be allowed without the restriction you
> propose. I thought that this was rejected in 4.00 because 4.00 used
> bi-variance as an (unsafe) approximation of non-injective. Since we now
> track injectivity separately from variance g be accepted (with 'a covariant).

In our work, this GADT definition would be accepted, and:
(1) matching on the constructor G does not give any information on the
value of the existential type 'a
(2) the parameter of g (not 'a, the one marked _ above) may marked
covariant or invariant, because the constructor t is upward-closed but
not downward-closed (private types)

In particular, the following does not type-check:
  let extract : int t g -> int = function
    | G n -> n

The G constructor at type ('b t) has type (exists 'a ['a t <=> 'b].
'a); so in the G branch the type-checker introduces the hypothesis ('a
t <=> int t) for an existential variable 'a. This does not allows to
deduce ('a <=> int) and therefore the body does not type-check.

However, in the OCaml type system, GADT type equalities are understood
in the unification sense (because it is the one that mixes well with
the inference engine), so the constructor G at type ('b g) would have
type (exists 'a. ['a t = 'b]), with (=) in the unification sense. As
Jacques said, this means that we must either reject the definition of
('b g) as is currently done, or stop considering ('a t) injective.

PS: Note that you could understand type parameter constraints in the
same way, which makes them safe without requiring injectivity, but
Didier remarked that this will only delay failure as code writers
generally assume injectivity. It's before to fail at definition site
than at usage site if you know you should fail anyway.

On Tue, Apr 30, 2013 at 12:12 PM, Leo White <lpw25@cam.ac.uk> wrote:
> [Sorry if this is a repeat post]
>
>> But it seems to me that this contradicts the definition of injectivity.
>> Namely, if we follow your definition, and have 'a bivar = 'b bivar, then
>> clearly bivar is not injective.
>
> I think Gabriel meant that 'a bivar = 'b bivar were equal in the
> subtyiping relationship, rather than in the unification
> relationship. (Perhaps we should use a different symbol for subtyping
> equality).
>
>> So there are two solutions: either we do not allow a bi-variant type
>> to be injective (breaking our simple statement that concrete types
>> are injective in all their parameters), or we consider bi-variance +
>> injectivity is some intermediary state, where we can use both directions
>> of subtyping, but not strong (unification) equality.
>
> I don't see how this implies the need for the strengthening you
> describe. As I see it:
>
>   type 'a t = T
>
> creates a type that is bi-variant in its parameter, so all occurences of
> 'a in:
>
>   type 'a u = 'a t -> 'a t
>
> are in bi-variant positions, so u should also be bi-variant.
>
>> The practical reason is to make easier to define indices.
>> If we keep the bi-variance in an invariant context, then the following
>> type definition is refused:
>>
>>       type 'a t = T;;
>>       type _ g = G : 'a -> 'a t g;;
>>
>> In 4.00, this definition is refused because 'a in 'a t g is bi-variant, but 'a appears
>> in a covariant position.
>
> I don't see why this could not be allowed without the restriction you
> propose. I thought that this was rejected in 4.00 because 4.00 used
> bi-variance as an (unsafe) approximation of non-injective. Since we now
> track injectivity separately from variance g be accepted (with 'a covariant).

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30 11:30                     ` Gabriel Scherer
@ 2013-04-30 13:06                       ` Leo White
  0 siblings, 0 replies; 37+ messages in thread
From: Leo White @ 2013-04-30 13:06 UTC (permalink / raw)
  To: Gabriel Scherer; +Cc: Jacques Garrigue, OCaML List Mailing

>>>       type 'a t = T;;
>>>       type _ g = G : 'a -> 'a t g;;
>>
>> I don't see why this could not be allowed without the restriction you
>> propose. I thought that this was rejected in 4.00 because 4.00 used
>> bi-variance as an (unsafe) approximation of non-injective. Since we now
>> track injectivity separately from variance g be accepted (with 'a covariant).
>
> In our work, this GADT definition would be accepted, and:
> (1) matching on the constructor G does not give any information on the
> value of the existential type 'a
> (2) the parameter of g (not 'a, the one marked _ above) may marked
> covariant or invariant, because the constructor t is upward-closed but
> not downward-closed (private types)

I don't think the argument to G needs to be given an existential type,
as long as the parameter of g is marked invariant.

The parameter to g should be marked invariant for two reasons:
1) It is constrained in the result type of a GADT constructor which, as
discussed on this list previously, forces it to be invariant (at least
for now, see Gabriel's paper for further details).
2) Marking it as anything other than invariant, would entail marking
'a as bi-variant, when it is in fact covariant.

This second reason also occurs in types with constraints, for example:

  type 'a s = 'b constraint 'a = 'b t

here 'b is covariant (used in covariant and bi-variant positions), but
marking 'a as any variance other than invariant would entail marking 'b
as bi-variant.

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-30  5:45       ` Jacques Garrigue
@ 2013-05-04  6:46         ` Jacques Garrigue
  2013-05-04  7:09           ` Gabriel Scherer
  0 siblings, 1 reply; 37+ messages in thread
From: Jacques Garrigue @ 2013-05-04  6:46 UTC (permalink / raw)
  To: OCaML List Mailing

On 2013/04/30, at 14:45, Jacques Garrigue <garrigue@math.nagoya-u.ac.jp> wrote:

> I have now committed in trunk a fix to PR#5985.
> You can use it to test whether your codebase runs into problems.
> You can either obtain it from subversion directly
> 	svn checkout http://caml.inria.fr/svn/ocaml/trunk
> or use opam to do it for you.
> 
> I checked that at least Core itself compiles without problems.

Ironically, I have just found that lablgtk2 does not compile with the fixed version.
lablgtk2 does not use GADTs, but it uses constrained type parameters in classes.

The problem is as follows:

gobject.mli:
type -'a gobj

gContainer.mli:
class virtual ['a] item_container :
  object
    constraint 'a = < as_item : [>`widget] obj; .. >
    method add : 'a -> unit
    …
  end

File "gContainer.mli", line 126, characters 6-665:
Error: In this definition, a type variable cannot be deduced
       from the type parameters.

It is a bit surprising, since the said variable is the row variable
of the above [> `widget], and it is only referred through 'a.
The reason there is an error is that the checkability requirements
for the body of types and constrained type parameters differ:
to allow the row variable to appear in a position potentially
contravariant, we need it to know from the constraint that it
appears for sure in a negative or invariant position.
One way to solve this is to check that the type identified by 'a can
actually be handled as a real variable (its internal variables do
not occur out of it), which would solve the discrepancy.
However, checking this is going to be a pain.
And making gobj injective immediately solves the problem.

In the midtime, the problem is fixed in the git version of
lablgtk2 by pretending that gobj is a private sum type (whose
contents are abstract).

This also empasizes that the new restriction does not impact
only GADTs: all constrained type parameters are also concerned.

	Jacques Garrigue

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-05-04  6:46         ` Jacques Garrigue
@ 2013-05-04  7:09           ` Gabriel Scherer
  2013-05-04 12:28             ` Jacques Garrigue
  0 siblings, 1 reply; 37+ messages in thread
From: Gabriel Scherer @ 2013-05-04  7:09 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

> One way to solve this is to check that the type identified by 'a can
> actually be handled as a real variable (its internal variables do
> not occur out of it), which would solve the discrepancy.

If I understand correctly, you are saying that this "constraint", in
absence of injectivity, could be handled semantically like GADTs with
non-injective equations, that is as quantifying locally on a ("true")
existential variable?

Handling existential variables (in fact local type constructors)
attached to GADT constructors is well-understood as they have clear
introduction sites and scopes, exactly where their constructor is
matched. When you say that checking the "constraint" case is painful,
is it related to the fact that there is no such corresponding
term-level marker?

On Sat, May 4, 2013 at 8:46 AM, Jacques Garrigue
<garrigue@math.nagoya-u.ac.jp> wrote:
> On 2013/04/30, at 14:45, Jacques Garrigue <garrigue@math.nagoya-u.ac.jp> wrote:
>
>> I have now committed in trunk a fix to PR#5985.
>> You can use it to test whether your codebase runs into problems.
>> You can either obtain it from subversion directly
>>       svn checkout http://caml.inria.fr/svn/ocaml/trunk
>> or use opam to do it for you.
>>
>> I checked that at least Core itself compiles without problems.
>
> Ironically, I have just found that lablgtk2 does not compile with the fixed version.
> lablgtk2 does not use GADTs, but it uses constrained type parameters in classes.
>
> The problem is as follows:
>
> gobject.mli:
> type -'a gobj
>
> gContainer.mli:
> class virtual ['a] item_container :
>   object
>     constraint 'a = < as_item : [>`widget] obj; .. >
>     method add : 'a -> unit
>     …
>   end
>
> File "gContainer.mli", line 126, characters 6-665:
> Error: In this definition, a type variable cannot be deduced
>        from the type parameters.
>
> It is a bit surprising, since the said variable is the row variable
> of the above [> `widget], and it is only referred through 'a.
> The reason there is an error is that the checkability requirements
> for the body of types and constrained type parameters differ:
> to allow the row variable to appear in a position potentially
> contravariant, we need it to know from the constraint that it
> appears for sure in a negative or invariant position.
> One way to solve this is to check that the type identified by 'a can
> actually be handled as a real variable (its internal variables do
> not occur out of it), which would solve the discrepancy.
> However, checking this is going to be a pain.
> And making gobj injective immediately solves the problem.
>
> In the midtime, the problem is fixed in the git version of
> lablgtk2 by pretending that gobj is a private sum type (whose
> contents are abstract).
>
> This also empasizes that the new restriction does not impact
> only GADTs: all constrained type parameters are also concerned.
>
>         Jacques Garrigue
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-05-04  7:09           ` Gabriel Scherer
@ 2013-05-04 12:28             ` Jacques Garrigue
  0 siblings, 0 replies; 37+ messages in thread
From: Jacques Garrigue @ 2013-05-04 12:28 UTC (permalink / raw)
  To: Gabriel Scherer, Leo P White; +Cc: OCaML List Mailing

On 2013/05/04, at 16:09, Gabriel Scherer <gabriel.scherer@gmail.com> wrote:

>> One way to solve this is to check that the type identified by 'a can
>> actually be handled as a real variable (its internal variables do
>> not occur out of it), which would solve the discrepancy.
> 
> If I understand correctly, you are saying that this "constraint", in
> absence of injectivity, could be handled semantically like GADTs with
> non-injective equations, that is as quantifying locally on a ("true")
> existential variable?

I did not think of it that way, but this is something like that.
By abstracting on the (common) part of the types which contains
abstract type constructor, we can fall back to exact variance information.

> Handling existential variables (in fact local type constructors)
> attached to GADT constructors is well-understood as they have clear
> introduction sites and scopes, exactly where their constructor is
> matched. When you say that checking the "constraint" case is painful,
> is it related to the fact that there is no such corresponding
> term-level marker?

Well yes, you have to find the common parts yourself.
Actually this is not that bad.
I have committed a fix, which works by recursing on the body
of the definition, trying to find equal types inside the constrained
parameters. If the variance at this point is correct, one doesn't
need to look inside.
This may be a bit expensive, but usually constraints are not big.

In theory one could do even better, by abstracting on the "real"
variance of abstract types. Then the variance of an occurence
becomes a set of paths (composition of variances) from the root
of the type definition. However, this just looks too complicated to
me...

> On Sat, May 4, 2013 at 8:46 AM, Jacques Garrigue
> <garrigue@math.nagoya-u.ac.jp> wrote:
>> Ironically, I have just found that lablgtk2 does not compile with the fixed version.
>> lablgtk2 does not use GADTs, but it uses constrained type parameters in classes.
>> 
>> The problem is as follows:
>> 
>> gobject.mli:
>> type -'a gobj
>> 
>> gContainer.mli:
>> class virtual ['a] item_container :
>>  object
>>    constraint 'a = < as_item : [>`widget] obj; .. >
>>    method add : 'a -> unit
>>    …
>>  end
>> 
>> File "gContainer.mli", line 126, characters 6-665:
>> Error: In this definition, a type variable cannot be deduced
>>       from the type parameters.

This problem is now fixed, without needing to change lablgtk2 sources.

On 2013/04/30, at 22:06, Leo White <lpw25@cam.ac.uk> wrote:

>>>>      type 'a t = T;;
>>>>      type _ g = G : 'a -> 'a t g;;
>>> 
>>> I don't see why this could not be allowed without the restriction you
>>> propose. I thought that this was rejected in 4.00 because 4.00 used
>>> bi-variance as an (unsafe) approximation of non-injective. Since we now
>>> track injectivity separately from variance g be accepted (with 'a covariant).
>> 
>> In our work, this GADT definition would be accepted, and:
>> (1) matching on the constructor G does not give any information on the
>> value of the existential type 'a
>> (2) the parameter of g (not 'a, the one marked _ above) may marked
>> covariant or invariant, because the constructor t is upward-closed but
>> not downward-closed (private types)
> 
> I don't think the argument to G needs to be given an existential type,
> as long as the parameter of g is marked invariant.
> 
> The parameter to g should be marked invariant for two reasons:
> 1) It is constrained in the result type of a GADT constructor which, as
> discussed on this list previously, forces it to be invariant (at least
> for now, see Gabriel's paper for further details).
> 2) Marking it as anything other than invariant, would entail marking
> 'a as bi-variant, when it is in fact covariant.
> 
> This second reason also occurs in types with constraints, for example:
> 
>  type 'a s = 'b constraint 'a = 'b t
> 
> here 'b is covariant (used in covariant and bi-variant positions), but
> marking 'a as any variance other than invariant would entail marking 'b
> as bi-variant.

Sorry for the slow answer. The implementation was not correct yet...

I think there is a misunderstanding here.
The problem is that 'b has two variances: its internal one, inferred from
its occurrences inside the body of the type, and its external one, defined
by its occurences inside parameters of the type.
For the definition to be correct, we need the external variance to be
at least as rigid as the internal one.
The extra difficulty is that our knowledge of variances is only approximative
(due to abstraction), and we have to use lower bounds on external variances,
and an upper bounds on internal variances (to be sure that we are safe).
Here the lower bound says just that the external variance of 'b
is the composition of invariant and injective, while the internal one
is covariant. If invariant o injective were defined as only injective,
it would be less rigid than the internal version, hence the need to
have invariant o injective = invariant.
(But for the converse, we only have injective o invariant = injective)

Actually I realized that just positive, negative and injective are not sufficient.
To fully accommodate the need for upper and lower bounds, we end up
needing 7 different variance flags!
  may_pos, may_neg : the variable may appear at positive or negative
	occurrences in the real definition; this is an upper bound,
	corresponding to variance annotations on abstract types
  may_weak: needed to make type inference principal
  pos, neg: we now that the variable appears at positive or negative
	occurrences in the real definition; this is a lower bound
  inj: either pos, or neg, or parameter of a concrete definition;
	does not disappear by expansion
  inv: pos and neg simultaneously in a concrete definition; needed
	the above composition inv o inj = inv
We have the hierarchy unknown < inj < pos \/ neg < pos /\ neg < inv.
Of course not all combinations are valid, (for instance pos implies may_pos,
etc…) but this is still mind boggling.

But those are not sufficient to solve the lablgtk2 problem above:
calling 'b the row variable of [> `widget], its internal variance is
neg o pos o may_neg, but its external variance is inv o pos o may_neg.
The result is may_pos internally, but externally the result is
may_pos /\ may_neg, whose lower bound is just "unknown".

Hence the need to "abstract" on common types, when their internal
variables do not escape.
The variance of 'a internally is neg, and externally inv, so all is fine.

This is a bit complicated, but the full specification is clear enough.
Thanks to the detection of common types, the fact that the only flags
for abstract types are may_pos and may_neg is not too much
a problem for constrained parameters.

Jacques Garrigue

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-04-28  0:02 [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
                   ` (3 preceding siblings ...)
  2013-04-29  5:17 ` Jacques Le Normand
@ 2013-07-01 14:47 ` Alain Frisch
  2013-07-01 23:20   ` Jacques Garrigue
  4 siblings, 1 reply; 37+ messages in thread
From: Alain Frisch @ 2013-07-01 14:47 UTC (permalink / raw)
  To: Jacques Garrigue, OCaML List Mailing

On 04/28/2013 02:02 AM, Jacques Garrigue wrote:
> The fix is simple enough: we should track injectivity, and assume that abstract
> types are not injective by default.
> However, this also means that the first type I defined above (using Hashtbl.t)
> will be refused.
>
> How many of you are using GADTs in this way, and how dependent are you on
> abstract types ?

FWIW, it turns out that we have very recently introduced such a case 
(and I realized it while synchronizing our version of OCaml with the trunk).

We used to have a functor with this signature:

module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
     sig
       module type S = sig
         type s
         type t
         val t: t ttype
         val eq: (s, t T.t) TypEq.t
       end
       val check: 'a ttype -> (module S with type s = 'a) option
     end

ttype is our type representing type structures at runtime.  The functors 
returns a function that checks if a given runtime type represents an 
instance of an unary abstract type constructor passed in argument to the 
functor (the functors check that this is indeed an abstract type).   In 
case of success, the function returns the ttype of the type constructor 
argument.  The existential is encoded with a first-class module and the 
type-equality is encoded in the classical way (('a, 'b) TypEq.t 
witnesses the equality of 'a and 'b:  type (_, _) t = Eq: ('a, 'a) t).

A GADT was recently introduced to replace this with a more direct 
representation:

module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
     sig
       type _ is_t = Is: 'a ttype -> 'a T.t is_t
       val check: 'a ttype -> 'a is_t option
     end

The problem is that this doesn't work any more (because T.t is not 
injective).

For now, I think I'll use:


module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
     sig
       type _ is_t = Is: 'b ttype * ('a, 'b T.t) TypEq.t -> 'a is_t
       val is_t: 'a ttype -> 'a is_t option
     end


which is accepted and roughly equivalent (by opening the equality 
witness, one can retrieve the static equality 'a == 'b T.t).


Alain

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-07-01 14:47 ` Alain Frisch
@ 2013-07-01 23:20   ` Jacques Garrigue
  2013-07-03 16:08     ` Alain Frisch
  0 siblings, 1 reply; 37+ messages in thread
From: Jacques Garrigue @ 2013-07-01 23:20 UTC (permalink / raw)
  To: Alain Frisch; +Cc: OCaML List Mailing

On 2013/07/01, at 23:47, Alain Frisch <alain@frisch.fr> wrote:

> A GADT was recently introduced to replace this with a more direct representation:
> 
> module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
>    sig
>      type _ is_t = Is: 'a ttype -> 'a T.t is_t
>      val check: 'a ttype -> 'a is_t option
>    end
> 
> The problem is that this doesn't work any more (because T.t is not injective).
> 
> For now, I think I'll use:
> 
> module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
>    sig
>      type _ is_t = Is: 'b ttype * ('a, 'b T.t) TypEq.t -> 'a is_t
>      val check : 'a ttype -> 'a is_t option
>    end
> 
> which is accepted and roughly equivalent (by opening the equality witness, one can retrieve the static equality 'a == 'b T.t).

Nice natural example, and nice workaround.

Of course, this is not strictly equivalent.
For instance suppose the following function:
  let f (type a) (tt : a T.t ttype) =
     match check tt with None -> assert false
     | Some (Is (tta, Eq)) -> (tta : a ttype)
The pattern matching will succeed, but tta will only have type "a ttype"
if T.t is injective. The nice part is that this is delayed to the use site,
where we may have more information about T.t, so this trick may still
be useful after introducing injectivity annotations.

Anyway, I suppose that this will work fine for you: check is intended
to be called on unknown types, so the missing equality should not
be a problem.

Jacques

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-07-01 23:20   ` Jacques Garrigue
@ 2013-07-03 16:08     ` Alain Frisch
  2013-07-03 16:13       ` Gabriel Scherer
  2013-07-04  1:00       ` [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
  0 siblings, 2 replies; 37+ messages in thread
From: Alain Frisch @ 2013-07-03 16:08 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

On 07/02/2013 01:20 AM, Jacques Garrigue wrote:
> On 2013/07/01, at 23:47, Alain Frisch <alain@frisch.fr> wrote:
>
>> A GADT was recently introduced to replace this with a more direct representation:
>>
>> module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
>>     sig
>>       type _ is_t = Is: 'a ttype -> 'a T.t is_t
>>       val check: 'a ttype -> 'a is_t option
>>     end
>>
>> The problem is that this doesn't work any more (because T.t is not injective).
>>
>> For now, I think I'll use:
>>
>> module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
>>     sig
>>       type _ is_t = Is: 'b ttype * ('a, 'b T.t) TypEq.t -> 'a is_t
>>       val check : 'a ttype -> 'a is_t option
>>     end
>>
>> which is accepted and roughly equivalent (by opening the equality witness, one can retrieve the static equality 'a == 'b T.t).
>
> Nice natural example, and nice workaround.

Btw, this is also an example where type-based disambiguation in presence 
of a GADT would be useful.  For instance, we currently need to write:

     match StringMap_matcher.check t with
     | Some (StringMap_matcher.Is (s, TypEq.Eq)) -> vmap (StringMap.map 
(of_value ~t:s env) x)
     | None -> ...

while one could write:

     match StringMap_matcher.check t with
     | Some (Is (s, Eq)) -> vmap (StringMap.map (of_value ~t:s env) x)
     | None -> ...

(see http://caml.inria.fr/mantis/view.php?id=6023 )

> Of course, this is not strictly equivalent.
> For instance suppose the following function:
>    let f (type a) (tt : a T.t ttype) =
>       match check tt with None -> assert false
>       | Some (Is (tta, Eq)) -> (tta : a ttype)
> The pattern matching will succeed, but tta will only have type "a ttype"
> if T.t is injective. The nice part is that this is delayed to the use site,
> where we may have more information about T.t, so this trick may still
> be useful after introducing injectivity annotations.
>
> Anyway, I suppose that this will work fine for you: check is intended
> to be called on unknown types, so the missing equality should not
> be a problem.

Indeed!

In some cases (in particular to add annotations to make sense of error 
messages), it would be useful to be able to name the type constructor 
introduced by opening the GADT to match the existential type.  (I'd put 
this rather high on my wish list around GADTs!)


Alain

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-07-03 16:08     ` Alain Frisch
@ 2013-07-03 16:13       ` Gabriel Scherer
  2013-07-04  6:07         ` [Caml-list] Request for feedback: A problem with injectivity oleg
  2013-07-04  1:00       ` [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
  1 sibling, 1 reply; 37+ messages in thread
From: Gabriel Scherer @ 2013-07-03 16:13 UTC (permalink / raw)
  To: Alain Frisch; +Cc: Jacques Garrigue, OCaML List Mailing

> In some cases (in particular to add annotations to make sense of error
> messages), it would be useful to be able to name the type constructor
> introduced by opening the GADT to match the existential type.  (I'd put this
> rather high on my wish list around GADTs!)

Indeed, virtually anyone that used GADTs in OCaml encountered that
problem and has a way to name existential type high on his/her
wishlist. We discussed a few ideas in the following bugtracking
dicussion:

  http://caml.inria.fr/mantis/view.php?id=5780

On Wed, Jul 3, 2013 at 6:08 PM, Alain Frisch <alain@frisch.fr> wrote:
> On 07/02/2013 01:20 AM, Jacques Garrigue wrote:
>>
>> On 2013/07/01, at 23:47, Alain Frisch <alain@frisch.fr> wrote:
>>
>>> A GADT was recently introduced to replace this with a more direct
>>> representation:
>>>
>>> module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
>>>     sig
>>>       type _ is_t = Is: 'a ttype -> 'a T.t is_t
>>>       val check: 'a ttype -> 'a is_t option
>>>     end
>>>
>>> The problem is that this doesn't work any more (because T.t is not
>>> injective).
>>>
>>> For now, I think I'll use:
>>>
>>> module ABSTRACT_1_MATCHER (T : sig type 'a t val t: unit t ttype end) :
>>>     sig
>>>       type _ is_t = Is: 'b ttype * ('a, 'b T.t) TypEq.t -> 'a is_t
>>>       val check : 'a ttype -> 'a is_t option
>>>     end
>>>
>>> which is accepted and roughly equivalent (by opening the equality
>>> witness, one can retrieve the static equality 'a == 'b T.t).
>>
>>
>> Nice natural example, and nice workaround.
>
>
> Btw, this is also an example where type-based disambiguation in presence of
> a GADT would be useful.  For instance, we currently need to write:
>
>     match StringMap_matcher.check t with
>     | Some (StringMap_matcher.Is (s, TypEq.Eq)) -> vmap (StringMap.map
> (of_value ~t:s env) x)
>     | None -> ...
>
> while one could write:
>
>     match StringMap_matcher.check t with
>     | Some (Is (s, Eq)) -> vmap (StringMap.map (of_value ~t:s env) x)
>     | None -> ...
>
> (see http://caml.inria.fr/mantis/view.php?id=6023 )
>
>
>> Of course, this is not strictly equivalent.
>> For instance suppose the following function:
>>    let f (type a) (tt : a T.t ttype) =
>>       match check tt with None -> assert false
>>       | Some (Is (tta, Eq)) -> (tta : a ttype)
>> The pattern matching will succeed, but tta will only have type "a ttype"
>> if T.t is injective. The nice part is that this is delayed to the use
>> site,
>> where we may have more information about T.t, so this trick may still
>> be useful after introducing injectivity annotations.
>>
>> Anyway, I suppose that this will work fine for you: check is intended
>> to be called on unknown types, so the missing equality should not
>> be a problem.
>
>
> Indeed!
>
> In some cases (in particular to add annotations to make sense of error
> messages), it would be useful to be able to name the type constructor
> introduced by opening the GADT to match the existential type.  (I'd put this
> rather high on my wish list around GADTs!)
>
>
> Alain
>
>
> --
> Caml-list mailing list.  Subscription management and archives:
> https://sympa.inria.fr/sympa/arc/caml-list
> Beginner's list: http://groups.yahoo.com/group/ocaml_beginners
> Bug reports: http://caml.inria.fr/bin/caml-bugs

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-07-03 16:08     ` Alain Frisch
  2013-07-03 16:13       ` Gabriel Scherer
@ 2013-07-04  1:00       ` Jacques Garrigue
  2013-07-04  8:14         ` Alain Frisch
  1 sibling, 1 reply; 37+ messages in thread
From: Jacques Garrigue @ 2013-07-04  1:00 UTC (permalink / raw)
  To: Alain Frisch; +Cc: OCaML List Mailing

On 2013/07/04, at 1:08, Alain Frisch <alain@frisch.fr> wrote:

> Btw, this is also an example where type-based disambiguation in presence of a GADT would be useful.  For instance, we currently need to write:
> 
>    match StringMap_matcher.check t with
>    | Some (StringMap_matcher.Is (s, TypEq.Eq)) -> vmap (StringMap.map (of_value ~t:s env) x)
>    | None -> ...
> 
> while one could write:
> 
>    match StringMap_matcher.check t with
>    | Some (Is (s, Eq)) -> vmap (StringMap.map (of_value ~t:s env) x)
>    | None -> ...
> 
> (see http://caml.inria.fr/mantis/view.php?id=6023 )

In theory, it would be OK to assume that all pattern-matchings may contain GADT type constructors.
The only real problem is with let's: if they contain GADTs, we must use the code for pattern-matching, which doesn't handle unused definition warnings in the same way… Having all non recursive let's go through this code would have a high impact on warnings.

If unused definition warnings could be separated from type checking this would be much easier.

> In some cases (in particular to add annotations to make sense of error messages), it would be useful to be able to name the type constructor introduced by opening the GADT to match the existential type.  (I'd put this rather high on my wish list around GADTs!)

I did to.
As Gabriel mentioned, there was a discussion about syntax on Mantis.

 http://caml.inria.fr/mantis/view.php?id=5780

As is often the case with syntax, this ended with no agreement, so there is no such feature…

Actually, the problem with syntax is a bit deeper than that.
Namely, my real concern is that currently locally abstract types cannot handle abstract row types.
I think this is a severe limitation, at least for some applications.
Maybe this problem should be solved first, as adding new syntax for existential types without solving this will only make it deeper.

	Jacques

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

* Re: [Caml-list] Request for feedback: A problem with injectivity
  2013-07-03 16:13       ` Gabriel Scherer
@ 2013-07-04  6:07         ` oleg
  2013-07-04  7:35           ` Alain Frisch
  0 siblings, 1 reply; 37+ messages in thread
From: oleg @ 2013-07-04  6:07 UTC (permalink / raw)
  To: gabriel.scherer; +Cc: garrigue, caml-list


> Indeed, virtually anyone that used GADTs in OCaml encountered that
> problem and has a way to name existential type high on his/her
> wishlist. We discussed a few ideas in the following bugtracking
> dicussion:
>
>   http://caml.inria.fr/mantis/view.php?id=5780

Just in case, here is the error message produced by GHC for the
equivalent example. 

Code:

{-# LANGUAGE GADTs #-}

data E a where
    B :: Bool -> E Bool
    A :: E (a -> b) -> E a -> E b

eval :: E a -> a
eval (B x)   = x
eval (A f x) = (eval f) x

The error message:

/tmp/ga.hs:9:25:
    Couldn't match type `a1' with `E a1'
      `a1' is a rigid type variable bound by
           a pattern with constructor
             A :: forall b a. E (a -> b) -> E a -> E b,
           in an equation for `eval'
           at /tmp/ga.hs:9:7
    In the second argument of `eval', namely `x'
    In the expression: (eval f) x
    In an equation for `eval': eval (A f x) = (eval f) x

The gist is using the type variable name 'a' from the definition of
GADT, and attaching the numeric suffix to disambiguate. (GHC does that all
the time -- and I believe OCaml does something similar in many cases,
only not for GADTs.) What helps in particular I believe is quoting the
clause from the GADT declaration, so we can see where the name 'a'
comes from. So, perhaps if the error message can be improved with
giving more information about the error, the problem will be solved
without additional syntax?




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

* Re: [Caml-list] Request for feedback: A problem with injectivity
  2013-07-04  6:07         ` [Caml-list] Request for feedback: A problem with injectivity oleg
@ 2013-07-04  7:35           ` Alain Frisch
  2013-07-05 10:30             ` oleg
  0 siblings, 1 reply; 37+ messages in thread
From: Alain Frisch @ 2013-07-04  7:35 UTC (permalink / raw)
  To: oleg, gabriel.scherer; +Cc: garrigue, caml-list

On 07/04/2013 08:07 AM, oleg@okmij.org wrote:
> The gist is using the type variable name 'a' from the definition of
> GADT, and attaching the numeric suffix to disambiguate. (GHC does that all
> the time -- and I believe OCaml does something similar in many cases,
> only not for GADTs.) What helps in particular I believe is quoting the
> clause from the GADT declaration, so we can see where the name 'a'
> comes from. So, perhaps if the error message can be improved with
> giving more information about the error, the problem will be solved
> without additional syntax?

Naming types created by opening GADT existentials is useful not only for 
error messages, but also to have a way to refer to that type in the 
branch (for instance to instantiate locally a functor whose argument 
refers to that type).

-- Alain

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-07-04  1:00       ` [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
@ 2013-07-04  8:14         ` Alain Frisch
  2013-07-04  8:52           ` Jacques Garrigue
  0 siblings, 1 reply; 37+ messages in thread
From: Alain Frisch @ 2013-07-04  8:14 UTC (permalink / raw)
  To: Jacques Garrigue; +Cc: OCaML List Mailing

On 07/04/2013 03:00 AM, Jacques Garrigue wrote:
> In theory, it would be OK to assume that all pattern-matchings may contain GADT type constructors.
> The only real problem is with let's: if they contain GADTs, we must use the code for pattern-matching, which doesn't handle unused definition warnings in the same way… Having all non recursive let's go through this code would have a high impact on warnings.

If this is the only problem with the direct approach (not depending on 
backtracking), maybe the treatment of warnings can be parametrized to 
behave as expected?


> If unused definition warnings could be separated from type checking this would be much easier.

Maybe I'm wrong, but it seems to me that achieving the same behavior 
would require to duplicate of lot of the "data-flow" logic in the 
type-checker, thus increasing the global complexity (and the risk to 
have diverging behaviors).



Alain

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

* Re: [Caml-list] Request for feedback: A problem with injectivity and GADTs
  2013-07-04  8:14         ` Alain Frisch
@ 2013-07-04  8:52           ` Jacques Garrigue
  0 siblings, 0 replies; 37+ messages in thread
From: Jacques Garrigue @ 2013-07-04  8:52 UTC (permalink / raw)
  To: Alain Frisch; +Cc: OCaML List Mailing

On 2013/07/04, at 17:14, Alain Frisch <alain@frisch.fr> wrote:

> On 07/04/2013 03:00 AM, Jacques Garrigue wrote:
>> In theory, it would be OK to assume that all pattern-matchings may contain GADT type constructors.
>> The only real problem is with let's: if they contain GADTs, we must use the code for pattern-matching, which doesn't handle unused definition warnings in the same way… Having all non recursive let's go through this code would have a high impact on warnings.
> 
> If this is the only problem with the direct approach (not depending on backtracking), maybe the treatment of warnings can be parametrized to behave as expected?

This does not remove the need for backtracking, as the backtracking occurs in the code for typing pattern-matching.
It is needed to detect where GADTs are actually used.
(By the way this backtracking concerns only the typing of pattern-matching, and it is not costly, so there is no real need to avoid it).
The goal is to allow to switch between the code for let-in and the code for match freely.

>> If unused definition warnings could be separated from type checking this would be much easier.
> 
> Maybe I'm wrong, but it seems to me that achieving the same behavior would require to duplicate of lot of the "data-flow" logic in the type-checker, thus increasing the global complexity (and the risk to have diverging behaviors).

I just mean that if it could abstracted so that one could call the same use-tracking function for type_cases and type_let, things would be simpler. The goal here is factoring, not duplicating.

Jacques Garrigue

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

* Re: [Caml-list] Request for feedback: A problem with injectivity
  2013-07-04  7:35           ` Alain Frisch
@ 2013-07-05 10:30             ` oleg
  2013-07-05 12:02               ` Alain Frisch
  0 siblings, 1 reply; 37+ messages in thread
From: oleg @ 2013-07-05 10:30 UTC (permalink / raw)
  To: alain; +Cc: garrigue, caml-list


> Naming types created by opening GADT existentials is useful not only for 
> error messages, but also to have a way to refer to that type in the 
> branch (for instance to instantiate locally a functor whose argument 
> refers to that type).

That is true, but isn't this a separate problem? Do you often need to
refer to the type in a branch? On the other hand, good error messages
are needed often, or all the time. 

BTW, GHC does allow naming the type of the existential (I can't
remember the last time I used that for real though). For example,

data E a where
    B :: Bool -> E Bool
    A :: E (a -> b) -> E a -> E b

eval :: E a -> a
eval (B x)   = x
eval (A (f::E (u->v)) x) = (eval f) ((eval x)::u)

Here, I named the type of the argument, which is quantified by the
existential, as u. However, GHC does not use those types in the error
message. For example, if I make a mistake

eval :: E a -> a
eval (B x)   = x
eval (A (f::E (u->v)) x) = (eval f) (x::u)

I get the error message which talks about the type a
/tmp/ga.hs:9:38:
    Couldn't match type `a1' with `E a1'
      `a1' is a rigid type variable bound by
           a pattern with constructor
             A :: forall b a. E (a -> b) -> E a -> E b,
           in an equation for `eval'
           at /tmp/ga.hs:9:7
    In the second argument of `eval', namely `(x :: u)'
    In the expression: (eval f) (x :: u)
    In an equation for `eval':
        eval (A (f :: E (u -> v)) x) = (eval f) (x :: u)

I would argue that is reasonable: the error message quotes the
declaration of GADT; it is only logical it would use the types from
that declaration.



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

* Re: [Caml-list] Request for feedback: A problem with injectivity
  2013-07-05 10:30             ` oleg
@ 2013-07-05 12:02               ` Alain Frisch
  0 siblings, 0 replies; 37+ messages in thread
From: Alain Frisch @ 2013-07-05 12:02 UTC (permalink / raw)
  To: oleg; +Cc: garrigue, caml-list

On 07/05/2013 12:30 PM, oleg@okmij.org wrote:
>> Naming types created by opening GADT existentials is useful not only for
>> error messages, but also to have a way to refer to that type in the
>> branch (for instance to instantiate locally a functor whose argument
>> refers to that type).
>
> That is true, but isn't this a separate problem? Do you often need to
> refer to the type in a branch? On the other hand, good error messages
> are needed often, or all the time.

To be clear: the GHC way to report errors seems good to me.  But even if 
the default error message is improved, it is still helpful to be able to 
name the existential type simply to be able to write a type annotation 
mentioning that type somewhere deep in the branch (as a way to track 
down the actual error).  Typically, only a very small number of #exN 
types exist in a given scope, so the fact that the error message does 
not tell where they come from is not a blocking point in making sense of 
the error, and not being able to name the type is more problematic.

And of course, there are many other cases where it is useful to name the 
type, not only to track errors, but as ways to document the code or 
simply to be able to write it at all (especially when using local modules).

And to answer your question: yes, I've had to introduce a local 
polymorphic function (with a locally abstract type) quite often to 
work-around the lack of naming facility.

-- Alain

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

end of thread, other threads:[~2013-07-05 12:02 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-04-28  0:02 [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
2013-04-28  2:45 ` Markus Mottl
2013-04-28 10:28   ` Jacques Garrigue
2013-04-28  5:54 ` Jacques Le Normand
2013-04-29  3:45 ` Ivan Gotovchits
2013-04-29  4:03   ` Ivan Gotovchits
2013-04-29  5:17 ` Jacques Le Normand
2013-04-29  7:58   ` Alain Frisch
2013-04-29 10:52     ` Jacques Garrigue
2013-04-29 11:23       ` Alain Frisch
2013-04-29 16:37         ` Nathan Mishra Linger
2013-04-29 23:53           ` Jacques Garrigue
2013-04-30  5:45       ` Jacques Garrigue
2013-05-04  6:46         ` Jacques Garrigue
2013-05-04  7:09           ` Gabriel Scherer
2013-05-04 12:28             ` Jacques Garrigue
2013-04-30  6:59       ` Alain Frisch
2013-04-30  7:56         ` Jacques Garrigue
2013-04-30  8:02           ` Alain Frisch
2013-04-30  8:18             ` Jacques Garrigue
2013-04-30  9:11               ` Gabriel Scherer
2013-04-30  9:55                 ` Jacques Garrigue
2013-04-30 10:12                   ` Leo White
2013-04-30 11:30                     ` Gabriel Scherer
2013-04-30 13:06                       ` Leo White
2013-04-29  7:59   ` Gabriel Scherer
2013-07-01 14:47 ` Alain Frisch
2013-07-01 23:20   ` Jacques Garrigue
2013-07-03 16:08     ` Alain Frisch
2013-07-03 16:13       ` Gabriel Scherer
2013-07-04  6:07         ` [Caml-list] Request for feedback: A problem with injectivity oleg
2013-07-04  7:35           ` Alain Frisch
2013-07-05 10:30             ` oleg
2013-07-05 12:02               ` Alain Frisch
2013-07-04  1:00       ` [Caml-list] Request for feedback: A problem with injectivity and GADTs Jacques Garrigue
2013-07-04  8:14         ` Alain Frisch
2013-07-04  8:52           ` Jacques Garrigue

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).