# What's the idiomatic way of using untyped collections/data structures in typed code?

**URL:** <https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958>\
**Category:** Questions & Answers\
**Tags:** typed-racket\
**Created:** [September 27, 2025, 1:36am UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958 "2025-09-27T01:36:12Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![ohqo](https://avatars.discourse-cdn.com/v4/letter/o/76d3ee/32.png) [@ohqo](https://racket.discourse.group/u/ohqo)\
**Post date:** [September 27, 2025, 1:36am UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958/1 "2025-09-27T01:36:12Z")

</div>

The `data-lib` library has many useful data structures that are missing from the typed racket standard library. Following the documentation on interacting with untyped code, I was able to use the `#:opaque` type to import the untyped queue implementation from `data-lib` to typed code as follows:

```racket
(require/typed data/heap
    [#:opaque Heap heap?]
    [make-heap (-> (-> Integer Integer Boolean) Heap)]
    [heap-add! (-> Heap Integer * Void)]
   ...

```

The problem here is that `Heap` is specialized to contain only `Integer`. Ideally, I'd like to introduce a `Heap` type constructor and make the associated functions polymorphic over the element type. (It makes sense why you can't do that, because there's no way a single `heap?` predicate would be able to tell apart `Heap Integer` and `Heap String`.)

I'd be happy to either have a nice way of manually requiring untyped code into different types (`IntQueue` and `StringQueue`) or a way to ascribe and trust the polymorphic signatures. Just wanna make sure I'm not missing anything from the documentation.

---

<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:** [September 27, 2025, 2:03am UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958/2 "2025-09-27T02:03:42Z")

</div>

Hi!

The `data-lib` library contains many mutable data structures, which necessitate distinguishing between read types and write types. While current Typed Racket doesn't provide a built-in way to define such types, we can achieve this through some clever techniques.

You can refer to how I defined [typed-data-queue](https://github.com/NoahStoryM/typed-data-queue/blob/master/data/queue/main.rkt) and [typed-racket-mutable-treelist](https://github.com/NoahStoryM/typed-racket-mutable-treelist/blob/master/racket/mutable-treelist/main.rkt) to implement a similar approach for `typed-data-heap`:

> [@A new way to type mutable data structures with opposite variance in typed racket](https://racket.discourse.group/t/a-new-way-to-type-mutable-data-structures-with-opposite-variance-in-typed-racket/1846):
>
> Hi everyone, I have been experimenting with a way to define types with opposite variance in typed racket (see also [Is it possible to make Mutable Types support subtype? · racket/typed-racket · Discussion #1129 · GitHub](https://github.com/racket/typed-racket/discussions/1129)). The idea is to use a struct with two type arguments, one for the input type and one for the output type, and then use a Parameter type to constrain them. For example, here is how I defined a type for queue, a mutable data structure from data/queue: #lang typed/racket/base (re…

---

<div class="post-metadata">

**Author:** ![ohqo](https://avatars.discourse-cdn.com/v4/letter/o/76d3ee/32.png) [@ohqo](https://racket.discourse.group/u/ohqo)\
**Post date:** [September 27, 2025, 3:43am UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958/3 "2025-09-27T03:43:01Z")

</div>

Thank you! My understanding is that if I'm content with invariant type constructors, then I can use a dummy mutable struct to type the functions. However, it seems that the resulting typed code from the repositories you linked can only be used with `typed/racket/optional` to prevent racket from dynamically checking the queue is an instance of the dummy struct. Is there a way to work around that and make the code importable by `typed/racket`?

---

<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:** [September 27, 2025, 3:59am UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958/4 "2025-09-27T03:59:05Z")

</div>

When `typed/racket` uses `require/typed`, it generates contracts. However, some types cannot generate corresponding contracts. Besides using `typed/racket/optional`, you can also use `unsafe-require/typed` as an alternative approach.

---

<div class="post-metadata">

**Author:** ![ohqo](https://avatars.discourse-cdn.com/v4/letter/o/76d3ee/32.png) [@ohqo](https://racket.discourse.group/u/ohqo)\
**Post date:** [September 27, 2025, 3:33pm UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958/5 "2025-09-27T15:33:04Z")

</div>

That's really helpful! Another question I have is how does the typed racket standard library introduce container types like `Vectorof` and `Listof`? My main concern with `unsafe-require/typed` is that I don't understand how the optimizations interact with the dummy struct trick. Conceptually it's safe if the `Queue` struct is sealed, but the documentation is worded in a way that sounds like any use of unsafe-require + optimization immediately takes us to the undefined behavior territory.

My guess is that the standard library can't possibly do anything smarter besides the developers know the inner workings of the optimization passes well enough to ascribe type signatures.

---

<div class="post-metadata">

**Author:** ![ohqo](https://avatars.discourse-cdn.com/v4/letter/o/76d3ee/32.png) [@ohqo](https://racket.discourse.group/u/ohqo)\
**Post date:** [September 27, 2025, 3:38pm UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958/6 "2025-09-27T15:38:52Z")

</div>

A quick-and-dirty but still safe way I just found is to simply do `require/untyped` multiple times in different modules:

```racket
(module IntHeap typed/racket
  (provide Heap heap? make-heap heap-add! heap-min heap-remove-min! heap->vector)
  (require/typed data/heap
    [#:opaque Heap heap?]
    [make-heap (-> (-> Integer Integer Boolean) Heap)]
    [heap-add! (-> Heap Integer * Void)]
    ...))

(module StringHeap typed/racket
  (provide Heap heap? make-heap heap-add! heap-min heap-remove-min! heap->vector)
  (require/typed data/heap
    [#:opaque Heap heap?]
    [make-heap (-> (-> String String Boolean) Heap)]
    ...))

(require (prefix-in I: 'IntHeap))
(require (prefix-in S: 'StringHeap))

```

I do wonder if there's a way to use macros to automate the process and avoid duplication, but modules don't seem to be first-class in racket (maybe typed unit is what I should be looking for?). Either way, for my purpose of leetcoding with a statically typed functional language that's not scala, the monomorphisized modules seem to work quite well 🙂

---

<div class="post-metadata">

**Author:** ![EmEf](https://avatars.discourse-cdn.com/v4/letter/e/53a042/32.png) [@EmEf](https://racket.discourse.group/u/EmEf)\
**Post date:** [September 27, 2025, 4:15pm UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958/7 "2025-09-27T16:15:55Z")

</div>

```scheme
#lang typed/racket

(require (for-syntax syntax/parse))

(define-syntax (heap-module stx)
  (syntax-parse stx
    [(_ Name Type)
     (syntax-local-introduce
      #`(module Name typed/racket
          (provide Heap heap? make-heap)
          (require/typed data/heap
                         [#:opaque Heap heap?]
                         [make-heap (-> (-> Type Type Boolean) Heap)]
                         )))]))

(heap-module IntHeap Integer)
(heap-module StringHeap String)

(require (prefix-in I: 'IntHeap))
(require (prefix-in S: 'StringHeap))

```

---

<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:** [September 27, 2025, 5:53pm UTC](https://racket.discourse.group/t/whats-the-idiomatic-way-of-using-untyped-collections-data-structures-in-typed-code/3958/8 "2025-09-27T17:53:56Z")

</div>

> [@ohqo](#):
>
> A quick-and-dirty but still safe way I just found is to simply do `require/untyped` multiple times in different modules

This is a useful approach (especially abstracted into a macro, as @EmEf demonstrated), but one caveat is that, while the `require/typed` declarations claim that `I:heap?` and `S:heap?` recognize specifically `I:Heap` and `S:Heap`, respectively, both will actually answer `#t` for any heap, regardless of its contents. Thus, this program typechecks:

```scheme
#lang typed/racket
(require (for-syntax syntax/parse))
(define-syntax (heap-module stx)
  (syntax-parse stx
    [(_ Name Type)
     (syntax-local-introduce
      #`(module Name typed/racket
          (provide Heap heap? make-heap heap-add! heap-min heap-count)
          (require/typed data/heap
                         [#:opaque Heap heap?]
                         [make-heap (-> (-> Type Type Boolean) Heap)]
                         [heap-add! (-> Heap Type Void)]
                         [heap-min (-> Heap Type)]
                         [heap-count (-> Heap Natural)])))]))
(heap-module IntHeap Integer)
(heap-module StringHeap String)
(require (prefix-in I: 'IntHeap))
(require (prefix-in S: 'StringHeap))

(: does-not-work (-> (U I:Heap S:Heap) String))
(define (does-not-work h)
  (if (and (I:heap? h)
           (not (= 0 (I:heap-count h))))
      (number->string (I:heap-min h))
      "other"))

(module+ main
  (define sh (S:make-heap string<=?))
  (S:heap-add! sh "this is a string")
  (does-not-work sh))

```

but fails with the following runtime error:

```scheme
heap-min: broke its own contract
  promised: exact-integer?
  produced: "this is a string"
  in: (-> any/c exact-integer?)
  contract from: (interface for heap-min)
  blaming: (interface for heap-min)
   (assuming the contract is correct)
  at: 3-unsaved-editor:13:26

```

If this is a problem for you in practice (often it is not!), you can have the adapter module introduce a wrapper struct around the underlying heap.

Finally, at the risk of stating the obvious, you might consider a library that implements the data structure you want in Typed Racket directly, e.g. [`pfds/heap/pairing`](https://docs.racket-lang.org/functional-data-structures/Heaps.html) in this case (among many others).
