Hello everyone, I want to share some thoughts I encountered when using Typed Racket. I don't have too deep theoretical foundation and knowledge accumulation, so it may be a little naive.
Recently, I want to use mlist
to modify the list data in some files. As a TR
fan, I certainly hope to use TR to complete this job. However, it is not easy to use mutable type in TR
. The difficulty comes from two problems:
- The current
TR
does not support the subtype relationship between mutable types (see Is it possible to make Mutable Types support subtype? 路 Discussion #1129 路 racket/typed-racket 路 GitHub), -
TR
can't generatepredicate
for mutable type, nor can it generatecontract
for related operators to use by untyped racket.
For problem 1, I think Alex Knauth has already given a solution in that discussion. But how to solve the second problem?
I think the root of the second question is that when we convert the type A
of the value x
to its supertype B
for processing, we lose some type information about x
. After that, TR
uses Occurrence Typing
to retrieve x
's' type information A
by predicate
.
And the key to the problem is that we can't define predicate
for many types, which makes it difficult to retrieve the original type after we perform type conversion on the values.
I wonder if it is possible to retain the original type information during type conversion? For example, like this:
#lang typed/racket
(require typed/racket/unsafe)
(unsafe-require/typed compatibility/mlist
[mlist (All (A) [-> A * (MListof A)])])
(define-type MList-Natural (Tag (MListof Natural)))
(define-type MList-Boolean (Tag (MListof Boolean)))
(define-predicate mlist-natural? MList-Natural)
(define-predicate mlist-boolean? MList-Boolean)
(define-type T (U Symbol String MList-Natural MList-Boolean))
(: sum [-> (MListof Natural) Natural])
(define sum (位 (mls) (if (null? mls) 0 (+ (mcar mls) (sum (mcdr mls))))))
(: f [-> T Number])
(define (f arg)
(cond [(string? arg) (string-length arg)]
[(symbol? arg) (string-length (symbol->string arg))]
[(mlist-boolean? arg) -123]
[(mlist-natural? arg) (sum arg)]))
(f (mlist)) ; -> (f '()) -> (sum '()) -> 0
(f (mlist #t)) ; -> -123
(f (mlist 1 2 3)) ; -> 6
When calculating (f'())
and converting the type of '()
to tag type MList-Natural
, TR
adds a tag to '()
, and then use mlist-natural?
to check the tag for type inference.
Here is one way I simulated:
#lang typed/racket
(require typed/racket/unsafe)
(unsafe-require/typed compatibility/mlist
[mlist (All (A) [-> A * (MListof A)])])
(struct mlist-natural ([value : (MListof Natural)]) #:type-name MList-Natural)
(struct mlist-boolean ([value : (MListof Boolean)]) #:type-name MList-Boolean)
(define-type T (U Symbol String MList-Natural MList-Boolean))
(: sum [-> (MListof Natural) Natural])
(define sum (位 (mls) (if (null? mls) 0 (+ (mcar mls) (sum (mcdr mls))))))
(: f [-> T Number])
(define (f arg)
(cond [(string? arg) (string-length arg)]
[(symbol? arg) (string-length (symbol->string arg))]
[(mlist-boolean? arg) -123]
[(mlist-natural? arg) (sum (mlist-natural-value arg))]))
(f (mlist-natural (mlist)))
(f (mlist-boolean (mlist #t)))
(f (mlist-natural (mlist 1 2 3)))
Recall that the reason I hope TR
can support tag types is that we can't define predicate
s for all types. There is a similar dilemma in untyped racket -- we can't solve all problems with flat contract
alone, so we need chaperone contract
.
I wonder if we can also support tag contract
?
#lang racket
(require compatibility/mlist)
(define mlist-natural/t (tag-contract (mlistof natural? #:flat? #f)))
(define mlist-boolean/t (tag-contract (mlistof boolean? #:flat? #f)))
(define mlist-natural? (tag-contract-predicate mlist-natural/t))
(define mlist-boolean? (tag-contract-predicate mlist-boolean/t))
(define T? (or/c symbol? string? mlist-natural/t mlist-boolean/t))
(define sum (位 (mls) (if (null? mls) 0 (+ (mcar mls) (sum (mcdr mls))))))
(define/contract (f arg)
(-> T? number?)
(cond [(string? arg) (string-length arg)]
[(symbol? arg) (string-length (symbol->string arg))]
[(mlist-boolean? arg) -123]
[(mlist-natural? arg) (sum arg)]))
(f (mlist)) ; -> (f '()) -> (sum '()) -> 0
(f (mlist #t)) ; -> -123
(f (mlist 1 2 3)) ; -> 6