2
votes

From The Typed Racket Guide, to define a Union Type, just use (define-type Some-Type (U Type1 Type2)).

To define Polymorphic Data Structures, use something like (define-type (Opt a) (U ...)).

I want to define a polymorphic binary tree

(define-type (Tree a) (U (Leaf a) Node))
(struct (a) Leaf ([val : a]))
(struct Node ([left : Tree] [right : Tree]))
(define t1 (Leaf 5))
(define t2 (Leaf 8))
(define t3 (Node t1 t2))

I was wondering why the type of t1 is Leaf not Tree, and how to make it be a Tree ?

> t1
- : (Leaf Positive-Byte)
#<Leaf>
2

2 Answers

4
votes

When you do this:

(define-type (Tree a) (U (Leaf a) Node))

You're defining Tree as a type constructor. You shouldn't think of Tree itself as a type, only (Tree Some-Concrete-Type) as a type. So rename it to Treeof:

(define-type (Treeof a) (U (Leaf a) Node))
(struct (a) Leaf ([val : a]))
(struct Node ([left : Treeof] [right : Treeof]))

Now the problem is clearer. The node struct expects a Treeof, but a tree of what? What you want is this:

(define-type (Treeof a) (U (Leaf a) (Node a)))
(struct (a) Leaf ([val : a]))
(struct (a) Node ([left : (Treeof a)] [right : (Treeof a)]))

Now your example works:

#lang typed/racket
(define-type (Treeof a) (U (Leaf a) (Node a)))
(struct (a) Leaf ([val : a]))
(struct (a) Node ([left : (Treeof a)] [right : (Treeof a)]))
(define t1 (Leaf 5))
(define t2 (Leaf 8))
(define t3 (Node t1 t2))
1
votes

Please see the reference about subtyping (https://docs.racket-lang.org/ts-guide/types.html#%28part._.Subtyping%29). Basically, when you use the union type, Racket doesn't destruct old types. For example, you could define a type (U String Number), but String and Number would still be usable. 1 would still be recognized as Number, but you can use it in any places that expects (U String Number) because Number is a subtype of (U String Number).

Similarly, t1 is a Leaf, so there's nothing wrong that Racket reports that t1 is a Leaf. However, Leaf is a subtype of Tree, so t1 could be used in places where Tree is expected.