# Managing \`cast\` performance penalty

**URL:** <https://racket.discourse.group/t/managing-cast-performance-penalty/905>\
**Category:** Questions & Answers\
**Tags:** question, typed-racket\
**Created:** [April 21, 2022, 2:22pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905 "2022-04-21T14:22:24Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [April 21, 2022, 2:22pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/1 "2022-04-21T14:22:24Z")

</div>

Hi,

After putting `cast` here and there in my Typed Racket code, I got curious to know how much it cost me in terms of runtime performance:

```scheme
#lang typed/racket

(time (for ([i (in-range 10000)])
        (+ 1 i)))

(time (for ([i (in-range 10000)])
        (+ 1 (cast i Integer))))

```

```scheme
cpu time: 0 real time: 0 gc time: 0
cpu time: 18 real time: 18 gc time: 2

```

Oh. It actually tends to get somewhat worse when I reload the same code multiple times (tested mainly in Racket Mode, but also in DrRacket):

```scheme
cpu time: 0 real time: 0 gc time: 0
cpu time: 18 real time: 18 gc time: 2
> 
cpu time: 0 real time: 0 gc time: 0
cpu time: 20 real time: 20 gc time: 3
> 
cpu time: 0 real time: 0 gc time: 0
cpu time: 35 real time: 35 gc time: 18
> 
cpu time: 0 real time: 0 gc time: 0
cpu time: 39 real time: 39 gc time: 22
> 
cpu time: 0 real time: 0 gc time: 0
cpu time: 69 real time: 69 gc time: 51
> 
cpu time: 0 real time: 0 gc time: 0
cpu time: 28 real time: 28 gc time: 12
> 
cpu time: 0 real time: 0 gc time: 0
cpu time: 20 real time: 20 gc time: 3
> 
cpu time: 0 real time: 0 gc time: 0
cpu time: 26 real time: 26 gc time: 8

```

Well, the [docs](https://docs.racket-lang.org/ts-reference/special-forms.html#%28form._%28%28lib._typed-racket%2Fbase-env%2Fprims..rkt%29._cast%29%29) did tell me that:

> The value is actually protected with two contracts. The second contract checks the new type, but the first contract is put there to enforce the old type, to protect higher-order uses of the value.

As I understand it, the performance penalty is therefore the effect of the contracts `cast` imposes.

Is there a way to manage the performance penalty of `cast`?

I find myself doing lots of conversions between `Integer` and `Positive-Integer` or `Nonnegative-Integer`, and I am getting vaguely worried.

---

<div class="post-metadata">

**Author:** ![dannypsnl](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/dannypsnl/32/917_2.png) [@dannypsnl](https://racket.discourse.group/u/dannypsnl)\
**Post date:** [April 21, 2022, 3:08pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/2 "2022-04-21T15:08:11Z")

</div>

Maybe waiting for Shallow typed racket?

---

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [April 21, 2022, 8:59pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/3 "2022-04-21T20:59:20Z")

</div>

Woow, I watched [Racket Con 2020 : Shallow Typed Racket - YouTube](https://www.youtube.com/watch?v=BCOuq0mc0E0) and it got me really interested!

Quite excited to see that @ben_greenman has done some recent and huge work on the [related PR](https://github.com/racket/typed-racket/pull/948). I realize that shallow types do not fit all situations, but I'll be watching this work closely.

Thank you @dannypsnl for the pointer!

---

<div class="post-metadata">

**Author:** ![capfredf](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/capfredf/32/935_2.png) [@capfredf](https://racket.discourse.group/u/capfredf)\
**Post date:** [April 21, 2022, 9:23pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/4 "2022-04-21T21:23:31Z")

</div>

if you wanted `downcasting`, I would recommend type predicates.

```scheme
(time (for ([i (in-range 10000)])
        (+ 1 i)))

(time (for ([i (in-range 10000)])
        (+ 1 (if (integer? i) i
                   (error 'hi)))))

```

output:

```scheme
cpu time: 0 real time: 0 gc time: 0
cpu time: 0 real time: 0 gc time: 0

```

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [April 21, 2022, 10:44pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/5 "2022-04-21T22:44:05Z")

</div>

> [@scolobb](#):
>
> I find myself doing lots of conversions between `Integer` and `Positive-Integer` or `Nonnegative-Integer` , and I am getting vaguely worried.

I would say that `cast` in Typed Racket is very rarely the right construct. I would take a look at [`assert`](https://docs.racket-lang.org/ts-reference/Utilities.html#%28def._%28%28lib._typed-racket%2Fbase-env%2Fextra-procs..rkt%29._assert%29%29), which encapsulates the pattern @capfredf suggested. In some cases [`ann`](https://docs.racket-lang.org/ts-reference/special-forms.html#%28form._%28%28lib._typed-racket%2Fbase-env%2Fprims..rkt%29._ann%29%29) and the [`#{e :: t}`](https://docs.racket-lang.org/ts-reference/special-forms.html#%28form._%28%28lib._typed-racket%2Fbase-env%2Fprims..rkt%29._ann%29%29) reader syntax have also been useful for people who thought they needed `cast`.

I can think of two main cases when `cast` is a good choice:

1. When you are dealing with a higher-order or mutable type that can't be fully verified by a flat predicate; and
2. When you are receiving some structured data with a type like `Any` and the contract system's good error messages are worth the cost of a one-time `cast` at the boundary crossing.

By far the main way I use `cast` in practice is to promote the result of `read` or `fasl->sexp`. Even then, though, I'll more often use `require/typed` to import the function in question with a type-specific name and contract.

---

<div class="post-metadata">

**Author:** ![simonls](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/simonls/32/170_2.png) [@simonls](https://racket.discourse.group/u/simonls)\
**Post date:** [April 21, 2022, 10:46pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/6 "2022-04-21T22:46:27Z")

</div>

I am not a heavy typed racket user, so I don't really know.  
But your solution looks to me, what I would call an assert and looking at the docs I saw there is an [assert](https://docs.racket-lang.org/ts-reference/Utilities.html#(def._((lib._typed-racket%2Fbase-env%2Fextra-procs..rkt)._assert))) in typed racket.

So my question is, is that assert the idiomatic way to do something similar in typed racket?  
I guess @LiberalArtist answered my question. (yes)

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [April 21, 2022, 11:41pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/7 "2022-04-21T23:41:31Z")

</div>

I ... don't suppose there's an existing categorization that would allow certain uses of `cast` to be automatically replaced with `assert`s, is there? I can see that just providing a special list for this one optimization would be fragile, but if there's an existing lists somewhere of flat-checkable-types, would it not make sense to rewrite casts as asserts? There must be something wrong with this proposal, but I don't know what it is.

---

<div class="post-metadata">

**Author:** ![capfredf](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/capfredf/32/935_2.png) [@capfredf](https://racket.discourse.group/u/capfredf)\
**Post date:** [April 22, 2022, 12:53am UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/8 "2022-04-22T00:53:32Z")

</div>

`(assert v p)` roughly [expands](https://github.com/racket/typed-racket/blob/master/typed-racket-lib/typed-racket/base-env/extra-procs.rkt#L13) to `(if (p v) v (error ...)`

---

<div class="post-metadata">

**Author:** ![samth](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/samth/32/3_2.png) [@samth](https://racket.discourse.group/u/samth)\
**Post date:** [April 22, 2022, 1:24am UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/9 "2022-04-22T01:24:51Z")

</div>

Mostly I recommend using cast only as a last resort. Typically you can add a dynamic check using occurrence typing and then specify your own error behavior. If you don't want that control the assert function is the next best choice. Only if those don't work would I use cast.

---

<div class="post-metadata">

**Author:** ![LiberalArtist](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/liberalartist/32/151_2.png) [@LiberalArtist](https://racket.discourse.group/u/LiberalArtist)\
**Post date:** [April 22, 2022, 5:39pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/10 "2022-04-22T17:39:57Z")

</div>

It might be worth elaborating on this in the docs for [`cast`](https://docs.racket-lang.org/ts-reference/special-forms.html#%28form._%28%28lib._typed-racket%2Fbase-env%2Fprims..rkt%29._cast%29%29): it looks like they don't even link to [`assert`](https://docs.racket-lang.org/ts-reference/Utilities.html#%28def._%28%28lib._typed-racket%2Fbase-env%2Fextra-procs..rkt%29._assert%29%29), which is (reasonably) relegated to the "Utilities" section. I know this has come up before, perhaps for people familiar with languages where `cast` is a more frequently useful operation.

---

<div class="post-metadata">

**Author:** ![spdegabrielle](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/spdegabrielle/32/95_2.png) [@spdegabrielle](https://racket.discourse.group/u/spdegabrielle)\
**Post date:** [April 22, 2022, 7:47pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/11 "2022-04-22T19:47:09Z")

</div>

I don’t know this stuff enough to do my own pull request to update the docs, but I’ve done the odd one before and would be happy to help if you know what you want to write?

> <https://github.com/racket/typed-racket/blob/master/typed-racket-doc/typed-racket/scribblings/reference/special-forms.scrbl>

---

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [April 24, 2022, 1:53pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/12 "2022-04-24T13:53:30Z")

</div>

Thanks a lot for your answers everyone! I have been looking at the [5&nbsp;Occurrence Typing](https://docs.racket-lang.org/ts-guide/occurrence-typing.html) section in the Typed Racket reference for a couple years now, and have been wondering how on earth I managed to go around without using this feature, which is "One of Typed Racket’s distinguishing type system features." Well, I have my answer now 😃 The best news is that I totally understand your examples and the usefulness of occurrence typing, for the first time ever!

I managed to completely convert all `cast`s in my code to `assert`s, and it works like a charm. I have also just submitted a PR to Typed Racket (thanks @spdegabrielle for pointing the location!) to mention `assert`, occurrence typing, and `ann` in the docs of `cast`: [Mention assert and ann in the docs of cast. by scolobb · Pull Request #1231 · racket/typed-racket · GitHub](https://github.com/racket/typed-racket/pull/1231) .

* * *

While converting `cast`s to `assert`s, I found myself quite often using `assert` in conjuction with `make-predicate`, like so:

```scheme
(assert (modulo (+ x y) 2) (make-predicate (U Zero One)))

```

So I made myself a little macro:

```scheme
(define-syntax-parse-rule (assert-type e:expr type:expr)
  (assert e (make-predicate type)))

```

which is used like this

```scheme
(assert-type (modulo (+ x y) 2) (U Zero One))

```

Does that ring any bells? Like, do I miss an existing pattern already captured by a definition in Typed Racket, or is my macro somehow bad?

* * *

In what concerns `ann` and the `#{ e :: t }` reader extension, I frankly haven't yet found a use for them. I always write type annotations for top-level definitions, and Typed Racket is clever enough to figure out the types of the values in the definitions from that. Sometimes I need to incorporate some additional knowledge which is not directly derivable from the top-level types, and in these cases `assert` and `cast` do a good job, but `ann` is too weak, e.g.:

```scheme
> (define x : Any 1)
> (cast x Integer)
- : Integer
1
> (assert x (make-predicate Integer))
- : Integer
1
> (ann x Integer)
; tmp.rkt/cast-example:42:5: Type Checker: type mismatch
; expected: Integer
; given: Any
; in: x

```

Actually, I have even been wondering out of curiosity as to why there is a reader extension for `ann` specifically, but not for `cast` or `assert`.

---

<div class="post-metadata">

**Author:** ![NoahStoryM](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/noahstorym/32/17_2.png) [@NoahStoryM](https://racket.discourse.group/u/NoahStoryM)\
**Post date:** [April 24, 2022, 2:11pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/13 "2022-04-24T14:11:29Z")

</div>

Well, I usually use `ann` to annotate the type of `lambda`, and use reader extension to declare the type matched by `match`.

For example:

```scheme
> (map (ann (lambda (x) (symbol->string x)) (-> Symbol String)) '(a b c d))
- : (Pairof String (Listof String))
'("a" "b" "c" "d")

```

and here is an example from guide 3.1.4:

```scheme
(: assert-symbols! ((Listof Any) -> (Listof Symbol)))
(define (assert-symbols! lst)
  (match lst
    [(list (? symbol? #{s : (Listof Symbol)}) ...) s]
    [_ (error "expected only symbols, given" lst)]))

```

---

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [April 24, 2022, 2:30pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/14 "2022-04-24T14:30:55Z")

</div>

Thank you @NoahStoryM, these examples are illustrative and useful to me.

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [April 24, 2022, 9:02pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/15 "2022-04-24T21:02:21Z")

</div>

I keep trying to adopt this path, and then I find parts of it that just don't seem to work. Consider this example:

```scheme
#lang typed/racket

(: parse-let (Sexp -> (List (Listof Symbol)
                            (Listof Sexp)
                            Sexp)))
(define (parse-let lst)
  (match lst
    [(list 'let
           (list (? symbol? #{s : (Listof Symbol)})
                 '=
                 #{val : (Listof Sexp)})
           ...
           'in
           body)
     (list s val body)]
    [_ (error "expected only symbols, given" lst)]))

```

in 8.4.900, this signals the errors

```scheme
Type Checker: type mismatch
  expected: (Listof Symbol)
  given: (Listof Any) in: (match lst ((list (quote let) (list (? symbol? s) (quote =) val) ... (quote in) body) (list s val body)) (_ (error "expected only symbols, given" lst)))
. Type Checker: type mismatch
  expected: (Listof Sexp)
  given: (Listof Any) in: (match lst ((list (quote let) (list (? symbol? s) (quote =) val) ... (quote in) body) (list s val body)) (_ (error "expected only symbols, given" lst)))

```

... even though mousing over the identifier 's' shows it to have type `(Listof Symbol)`. My impression is that TR just gives up when the match becomes of sufficient complexity, which makes it hard to recommend this for students.

Am I missing something?

---

<div class="post-metadata">

**Author:** ![NoahStoryM](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/noahstorym/32/17_2.png) [@NoahStoryM](https://racket.discourse.group/u/NoahStoryM)\
**Post date:** [April 25, 2022, 12:10am UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/16 "2022-04-25T00:10:14Z")

</div>

Yes, I'm also impressed that `match` is so complicated that it becomes very difficult to directly annotate the type of the matched variable. This is why I basically only use #:when style now (as we discussed in [Is it good style to use `#:when` instead of `?` in match expression?](https://racket.discourse.group/t/is-it-good-style-to-use-when-instead-of-in-match-expression/779)).

And in TR, `match` seems not to design a syntax for declaring types, types can only be declared through reader extension (although I rarely do this, but I think this is at least an example to illustrate the use of reader extension)

---

<div class="post-metadata">

**Author:** ![samth](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/samth/32/3_2.png) [@samth](https://racket.discourse.group/u/samth)\
**Post date:** [April 25, 2022, 7:00pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/17 "2022-04-25T19:00:28Z")

</div>

The short answer is that using `...` in `match` when it is not the end of the list generates much more complex code, which TR doesn't understand. There is not a "give-up" threshold in any of these systems, just sometimes the output of macros is too hard to automatically infer the types.

Note that if you change this to more like Racket's syntax, with the bindings in a list, it typechecks fine.

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [April 25, 2022, 8:30pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/18 "2022-04-25T20:30:00Z")

</div>

Oh! Interesting. I should add "dots-not-at-the-end" to my list of things that substantially complicate the process of matching; that makes perfect sense.

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [April 25, 2022, 8:49pm UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/19 "2022-04-25T20:49:22Z")

</div>

Many thanks for reminding me of this. This is _so close_ to working in my classroom... but now I have a teeny tiny _different_ problem. Consider this code:

```scheme
#lang typed/racket

(: sexp? (Any -> Boolean : #:+ Sexp))
(define (sexp? s)
  (symbol? s))

(define (only-sexps [f : Any]) : Sexp
  (cond [(sexp? f) f]
        [else '(oops)]))

(: parse-let (Sexp -> (List (Listof Symbol)
                            (Listof Sexp)
                            Sexp)))

(define (parse-let lst)
  (match lst
    [(list 'let
           (list s '= val)
           ...
           'in
           body)
     #:when (and (andmap symbol? s)
                 (andmap sexp? val)) 
     (list s val body)]
    [_ (error "whatever")]))

```

Now, the type of `s` works perfectly, and it seems that TR can assign it the type Symbol. Unfortunately, Sexp is not a simple type, it requires a chaperone (apologies if I'm saying that incorrectly), so there's no flat `sexp?` check. In my case, I don't care about fancy things, I only want (what I believe to be) some pretty simple things, so I can possibly "roll my own" sexp?, but as this example shows (with a very very restrictive "sexp?" that actually only greenlights symbols), user-defined predicates don't seem to work in the same way as built-in ones like 'symbol?'. Again, I'd love to be wrong....

---

<div class="post-metadata">

**Author:** ![NoahStoryM](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/noahstorym/32/17_2.png) [@NoahStoryM](https://racket.discourse.group/u/NoahStoryM)\
**Post date:** [April 26, 2022, 3:20am UTC](https://racket.discourse.group/t/managing-cast-performance-penalty/905/20 "2022-04-26T03:20:48Z")

</div>

> this example shows (with a very very restrictive "sexp?" that actually only greenlights symbols), user-defined predicates don't seem to work in the same way as built-in ones like 'symbol?'.

I think the problem that your `sexp?` can't work like `symbol?` may be related to `andmap`.

```scheme
Welcome to Racket v8.4 [cs].
> (: sexp? (Any -> Boolean : #:+ Sexp))
> (define (sexp? s) (symbol? s))
> (: sexps? ((Listof Any) -> Boolean : #:+ (Listof Sexp)))
> (define (sexps? s) ((inst andmap Any Boolean Sexp) sexp? s))
string:1:19: Type Checker: type mismatch;
 mismatch in proposition
  expected: ((: s (Listof Sexp)) | Top)
  given: (Top | Top)
  in: ((inst andmap Any Boolean Sexp) sexp? s)
 [,bt for context]
>

```

TR seems to have special handling for the type of `andmap`, for example, we can define `listof?` directly through `andmap`:

```scheme
Welcome to Racket v8.4 [cs].
> (: listof? (All (A) (case-> [-> (pred A) (pred (Listof A))]
                              [-> [-> Any Boolean] [-> Any Boolean]])))
> (define listof?
    (λ (pred)
      (λ (arg)
        (and (list? arg)
             (andmap pred arg)))))
> (:print-type listof?)
(All (A)
  (case->
   (-> (-> Any Boolean : A) (-> Any Boolean : (Listof A)))
   (-> (-> Any Boolean) (-> Any Boolean))))
> (:print-type andmap)
(All (a c d b ...)
  (case->
   (-> (-> a c : d) (Listof a) c)
   (-> (-> a b ... b c) (Listof a) (Listof b) ... b (U True c))))
>

```

But if we manually retype `andmap`, this code will throw an error:

```scheme
Welcome to Racket v8.4 [cs].
> (require typed/racket/unsafe)
> (unsafe-require/typed racket/base
    [andmap
     (All (a c d b ...)
          (case->
           (-> (-> a c : d) (Listof a) c)
           (-> (-> a b ... b c) (Listof a) (Listof b) ... b (U True c))))])
> (: listof? (All (A) (case-> [-> (pred A) (pred (Listof A))]
                              [-> [-> Any Boolean] [-> Any Boolean]])))
> (define listof?
    (λ (pred)
      (λ (arg)
        (and (list? arg)
             (andmap pred arg)))))
string:5:11: Type Checker: Polymorphic function `andmap2' could not be applied to arguments:
Types: (-> a c : d) (Listof a) -> c
       (-> a b ... b c) (Listof a) (Listof b) ... b -> (U True c)
Arguments: (-> Any Boolean : A) (Listof Any)
Expected result: Boolean
  in: (andmap pred arg)
 [,bt for context]
> (:print-type andmap)
(All (a c d b ...)
  (case->
   (-> (-> a c : d) (Listof a) c)
   (-> (-> a b ... b c) (Listof a) (Listof b) ... b (U True c))))
>

```

> In my case, I don't care about fancy things, I only want (what I believe to be) some pretty simple things, so I can possibly "roll my own" sexp?

I don't think there's a safe way to implement `sexp?` in TR (and I'm not sure why you want only greenlights symbols predicates), I wonder if this weaker `sexp?` can solve the problem you met?

```scheme
(define-type S-Exp (U Boolean Real Char String Bytes
                      Keyword Symbol (Listof S-Exp)))
(define-predicate sexp? S-Exp)

```

> it requires a chaperone (apologies if I'm saying that incorrectly), so there's no flat sexp? check.

When I want to deal with mutable types, I'm always frustrated that I can't refine their types through predicates. But I thought before that maybe we could tag these non-flat values through contract and then get their original type information (see [[Feature Request] Tag Contract and Tag Type - #3 by NoahStoryM](https://racket.discourse.group/t/feature-request-tag-contract-and-tag-type/427/3)).

I don't know much about the specific implementation of contract and type, so I'm not sure whether tag type is feasible or not, hope this idea can help you.

[Next page](https://racket.discourse.group/t/managing-cast-performance-penalty/905.md?page=2)
