[racket-dev] Disjoint unions (from PR 13131)

2012-09-22 Thread Eli Barzilay
[This is unrelated to the PR, so redirected here.]

Yesterday, Vincent St-Amour wrote:
 
 Types like `Positive-Integer-Not-Fixnum' are used internally as building
 blocks for numeric types, but are not exported.
 
 IMO, these types wouldn't be very useful to users because
   - They're not used directly in the standard library (so using them
 wouldn't make types any more precise).
   - They don't correspond to very useful sets.
   - The set of numeric types TR provides is already somewhat overwhelming.
 
 These types aren't shown by TR's regular printer, so they're not usually
 a problem.
 
 However, as you point out, they still show up in some places, which is a
 problem. TR's printer is smart about unions that have names (such as
 `Integer') and displays them opaquely. `:type' exists specifically to
 peer inside these unions. Special-casing these internal types in `:type'
 (to avoid displaying them) could work, but it's not clear whether it's
 the right thing to do or not.

This whole thing sounds similar to what I think is needed to make
something like the disjoint union thing I use in my class available in
TR.  The thing that I think can work is (using my pl syntax) for this:

  (define-type SOMETHING
[This Integer]
[That String String])

to define two structs for `This' and `That', with constructors that
accept the specified number and type of arguments, and it also defines
`SOMETHING' as (U This That).  There's some additional syntax-level
information that makes it possible to do something like:

  (: foo : SOMETHING - Integer)
  (define (foo something)
(cases something
  [(This n) n]
  [(That s t) (+ (string-length s) (string-length t))]))

and complain if there's incomplete coverage or unreachable cases.
(And this functionality might be possible to implement with a `match'
if it's smart enough -- something that I always wished I could do,
to avoid the confusion between the two.  I didn't look into it, but
this might be possible using the new thing that Sam added recently,
where I'd add a last `_' clause with some raise a typecheck error if
you can get here.)

In any case, the main thing here is that `This' and `That' are
available as constructors, but the corresponding types are *not*
available.  This means that you cannot write code that handles just
`This', you must always deal with the whole `SOMETHING'.  This is an
intentional restriction, which makes some forms of hacking easier
since it forces you to deal with these complete types -- and it
happens that this kind of hacking is very common in PLAI-like
classes.  (Together with `cases', it makes a language feature that
makes it as natural to write such code as it is in ML/Haskell.)

-- 
  ((lambda (x) (x x)) (lambda (x) (x x)))  Eli Barzilay:
http://barzilay.org/   Maze is Life!
_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] Disjoint unions (from PR 13131)

2012-09-22 Thread Matthias Felleisen

I consider this problem distinct from Vincent's. 

I'd argue that the separate this/that constructors exist in your mind only. 




On Sep 22, 2012, at 7:23 AM, Eli Barzilay wrote:

 [This is unrelated to the PR, so redirected here.]
 
 Yesterday, Vincent St-Amour wrote:
 
 Types like `Positive-Integer-Not-Fixnum' are used internally as building
 blocks for numeric types, but are not exported.
 
 IMO, these types wouldn't be very useful to users because
  - They're not used directly in the standard library (so using them
wouldn't make types any more precise).
  - They don't correspond to very useful sets.
  - The set of numeric types TR provides is already somewhat overwhelming.
 
 These types aren't shown by TR's regular printer, so they're not usually
 a problem.
 
 However, as you point out, they still show up in some places, which is a
 problem. TR's printer is smart about unions that have names (such as
 `Integer') and displays them opaquely. `:type' exists specifically to
 peer inside these unions. Special-casing these internal types in `:type'
 (to avoid displaying them) could work, but it's not clear whether it's
 the right thing to do or not.
 
 This whole thing sounds similar to what I think is needed to make
 something like the disjoint union thing I use in my class available in
 TR.  The thing that I think can work is (using my pl syntax) for this:
 
  (define-type SOMETHING
[This Integer]
[That String String])
 
 to define two structs for `This' and `That', with constructors that
 accept the specified number and type of arguments, and it also defines
 `SOMETHING' as (U This That).  There's some additional syntax-level
 information that makes it possible to do something like:
 
  (: foo : SOMETHING - Integer)
  (define (foo something)
(cases something
  [(This n) n]
  [(That s t) (+ (string-length s) (string-length t))]))
 
 and complain if there's incomplete coverage or unreachable cases.
 (And this functionality might be possible to implement with a `match'
 if it's smart enough -- something that I always wished I could do,
 to avoid the confusion between the two.  I didn't look into it, but
 this might be possible using the new thing that Sam added recently,
 where I'd add a last `_' clause with some raise a typecheck error if
 you can get here.)
 
 In any case, the main thing here is that `This' and `That' are
 available as constructors, but the corresponding types are *not*
 available.  This means that you cannot write code that handles just
 `This', you must always deal with the whole `SOMETHING'.  This is an
 intentional restriction, which makes some forms of hacking easier
 since it forces you to deal with these complete types -- and it
 happens that this kind of hacking is very common in PLAI-like
 classes.  (Together with `cases', it makes a language feature that
 makes it as natural to write such code as it is in ML/Haskell.)
 
 -- 
  ((lambda (x) (x x)) (lambda (x) (x x)))  Eli Barzilay:
http://barzilay.org/   Maze is Life!
 _
  Racket Developers list:
  http://lists.racket-lang.org/dev

_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] Disjoint unions (from PR 13131)

2012-09-22 Thread Eli Barzilay
A few minutes ago, Matthias Felleisen wrote:
 
 I consider this problem distinct from Vincent's. 

Yes, the problem is separate (hence moving the discussion) -- it's the
feature that he mentioned (being able to hide types) that I was
referring to.


 I'd argue that the separate this/that constructors exist in your
 mind only.

I'm not following that...  If you're saying that the two constructors
are not separate, then I'm more than agreeing -- I'm saying that this
is the main feature of the whole thing: the fact that you cannot treat
them as separate constructors as far as the type system goes.  Using
plain structs so there's a separate `This' and `That' types is exactly
the thing that I want to remove from these type definitions.  To make
this even more concrete, if the types are hidden, then I cannot write
this (I'm overloading `This' as both the constructor name and the type
name):

  (: foo : Integer - This)
  (define (foo x) (This x))

because `This' is not bound as a type.  The nice feature that Vincent
describes (and that I didn't know about) is how TR will not show
hidden types without the explicit introspection tool, so even if I
leave things for TR's inference, I would see this on the REPL:

   (This 1)
  - : SOMETHING --- the type is not `This'
  (This 1)

so I'm not tempted to try to use `This' as a type somewhere.

-- 
  ((lambda (x) (x x)) (lambda (x) (x x)))  Eli Barzilay:
http://barzilay.org/   Maze is Life!
_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] Disjoint unions (from PR 13131)

2012-09-22 Thread Matthias Felleisen


On Sep 22, 2012, at 10:52 AM, Eli Barzilay wrote:

 A few minutes ago, Matthias Felleisen wrote:
 
 I consider this problem distinct from Vincent's. 
 
 Yes, the problem is separate (hence moving the discussion) -- it's the
 feature that he mentioned (being able to hide types) that I was
 referring to.
 
 
 I'd argue that the separate this/that constructors exist in your
 mind only.
 
 I'm not following that...  If you're saying that the two constructors
 are not separate, then I'm more than agreeing -- I'm saying that this
 is the main feature of the whole thing: the fact that you cannot treat
 them as separate constructors as far as the type system goes.

Correct. In ML/standard type terminology, This and That types (I meant types) 
are VARIANTS not types. It is only in TR's set-oriented type system, that they 
can play the role of types too. 

If you wanted to use define-type in plain TR programs (not '311'), you'd want 
to expose THIS and THAT. 


_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] Disjoint unions (from PR 13131)

2012-09-22 Thread Eli Barzilay
Just now, Matthias Felleisen wrote:
 
 On Sep 22, 2012, at 10:52 AM, Eli Barzilay wrote:
 
  I'm not following that...  If you're saying that the two
  constructors are not separate, then I'm more than agreeing -- I'm
  saying that this is the main feature of the whole thing: the fact
  that you cannot treat them as separate constructors as far as the
  type system goes.
 
 Correct. In ML/standard type terminology, This and That types (I
 meant types) are VARIANTS not types.

(Yes, and I use variants too with students -- I had to work hard to
avoid them showing up as types too, which would confuse students.)


 It is only in TR's set-oriented type system, that they can play the
 role of types too.

Obviously.


 If you wanted to use define-type in plain TR programs (not '311'),
 you'd want to expose THIS and THAT.

OK, so I think that we're saying the same thing as far as the course
context goes.  That, I think, is enough of a motivation to have such a
feature.

In a plain TR code context, I could rely on `match' to throw a type
error when I write:

  (: foo : SOMETHING - Integer)
  (define (foo something)
(match something
  [(This x) ...]))

This is assuming some variation of `match' that doesn't abort with an
error when it fails -- and I think that this is something that makes
much more sense in TR.  IOW, I think that it's better to make `match'
in TR throw a type error if the implicit error-throwing code is
reachable.

But I also see how I would use the feature for plain TR code too --
cases where I want to tell TR I never want to allow code that can
handle only `This' without using `That'.  Maybe a different way to
phrase this is that this is a restriction that becomes part of my
public (TR) API, and I see a value in allowing me to make that
restriction.


[You might think that I could apply the same argument for
`Positive-Integer-Not-Fixnum' -- but there the situation is that the
type is more hidden since it's an implementation detail that Vincent
doesn't want to leak out, so even doing some `cases'-like thing with
it is forbidden.]

-- 
  ((lambda (x) (x x)) (lambda (x) (x x)))  Eli Barzilay:
http://barzilay.org/   Maze is Life!
_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] Disjoint unions (from PR 13131)

2012-09-22 Thread Vincent St-Amour
At Sat, 22 Sep 2012 10:52:49 -0400,
Eli Barzilay wrote:
 
 A few minutes ago, Matthias Felleisen wrote:
  
  I consider this problem distinct from Vincent's. 
 
 Yes, the problem is separate (hence moving the discussion) -- it's the
 feature that he mentioned (being able to hide types) that I was
 referring to.
 
 
  I'd argue that the separate this/that constructors exist in your
  mind only.
 
 I'm not following that...  If you're saying that the two constructors
 are not separate, then I'm more than agreeing -- I'm saying that this
 is the main feature of the whole thing: the fact that you cannot treat
 them as separate constructors as far as the type system goes.  Using
 plain structs so there's a separate `This' and `That' types is exactly
 the thing that I want to remove from these type definitions.  To make
 this even more concrete, if the types are hidden, then I cannot write
 this (I'm overloading `This' as both the constructor name and the type
 name):
 
   (: foo : Integer - This)
   (define (foo x) (This x))
 
 The nice feature that Vincent
 describes (and that I didn't know about) is how TR will not show
 hidden types without the explicit introspection tool, so even if I
 leave things for TR's inference, I would see this on the REPL:
 
(This 1)
   - : SOMETHING --- the type is not `This'
   (This 1)

This is not what I'm describing.

If `(This 1)' is used as type `SOMETHING', the TR printer will print the
type as `SOMETHING', and not `(U This That)'. This is what I mean when I
say that the TR printer is smart about named unions.

The introspection tool allows you to look inside `SOMETHING'

 (:type SOMETHING)
(U This That)

In your example, `(This 1)' is not used as type `SOMETHING'. It's just a
`This', so its type gets printed as `This'. If you want all `This's to
always be considered as `SOMETHING's, you can use a wrapper around the
constructor:

(define (This-wrapper x) (Ann (This x) SOMETHING))

What I'm suggesting is that some unions (e.g. `Natural') be opaque even
to the introspection tool. Since there's no way to get something to
typecheck as `Positive-Integer-Not-Fixnum' (the typechecker will never
give that type to anything, it's just a trick to get more precise
intersections) showing it in `:type''s output is confusing.

Vincent
_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] Disjoint unions (from PR 13131)

2012-09-22 Thread Eli Barzilay
20 minutes ago, Vincent St-Amour wrote:
 At Sat, 22 Sep 2012 10:52:49 -0400,
 Eli Barzilay wrote:
  
 (This 1)
- : SOMETHING --- the type is not `This'
(This 1)
 
 This is not what I'm describing.
 
 If `(This 1)' is used as type `SOMETHING', the TR printer will print
 the type as `SOMETHING', and not `(U This That)'. This is what I
 mean when I say that the TR printer is smart about named unions.

I'm not sure what you mean by used as type `SOMETHING' -- I'm just
typing that expression in the REPL, and TR should use `SOMETHING' as
the type.  (And this actually works fine for me, though only for type
definitions with more than one variant.)


 [...] If you want all `This's to always be considered as
 `SOMETHING's, you can use a wrapper around the constructor:
 
 (define (This-wrapper x) (Ann (This x) SOMETHING))

(Hm, I didn't think about doing that...  But it would be hard to do
that without an `apply' now.)


 What I'm suggesting is that some unions (e.g. `Natural') be opaque
 even to the introspection tool. Since there's no way to get
 something to typecheck as `Positive-Integer-Not-Fixnum' (the
 typechecker will never give that type to anything, it's just a trick
 to get more precise intersections) showing it in `:type''s output is
 confusing.

That's mainly something that goes in the PR, since with my thing there
is some point in showing the hidden types.  But the similarity is how
the above translates to my case: I want a way to treat my user-defined
`SOMETHING' as opaque, and I want to hook into the typechecker a
restriction that TR would never give the hidden `This' type to
anything.  (And I think that I have that latter part, for =2
variants.)

-- 
  ((lambda (x) (x x)) (lambda (x) (x x)))  Eli Barzilay:
http://barzilay.org/   Maze is Life!
_
  Racket Developers list:
  http://lists.racket-lang.org/dev


Re: [racket-dev] Disjoint unions (from PR 13131)

2012-09-22 Thread Sam Tobin-Hochstadt
On Sat, Sep 22, 2012 at 12:20 PM, Eli Barzilay e...@barzilay.org wrote:

 What I'm suggesting is that some unions (e.g. `Natural') be opaque
 even to the introspection tool. Since there's no way to get
 something to typecheck as `Positive-Integer-Not-Fixnum' (the
 typechecker will never give that type to anything, it's just a trick
 to get more precise intersections) showing it in `:type''s output is
 confusing.

 That's mainly something that goes in the PR, since with my thing there
 is some point in showing the hidden types.  But the similarity is how
 the above translates to my case: I want a way to treat my user-defined
 `SOMETHING' as opaque, and I want to hook into the typechecker a
 restriction that TR would never give the hidden `This' type to
 anything.  (And I think that I have that latter part, for =2
 variants.)

But you need the type `This` to be used in typechecking the expansion
of `cases`, so that `This-field` selection works.
-- 
sam th
sa...@ccs.neu.edu
_
  Racket Developers list:
  http://lists.racket-lang.org/dev