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

From: Eli Barzilay (eli at barzilay.org)
Date: Sat Sep 22 07:23:58 EDT 2012

[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!

Posted on the dev mailing list.