You can wrap `stream-cons`, etc. in contracts and keep the original stream data type, but it's trickier than wrapping a normal function, and involves:
- an untyped file which imports the macros and exports "function-ized" versions of the macros
- a typed file which imports those function-ized things with require/typed, then exports new versions of the macros in terms of the functions
And looks something like like these two files:
; untyped-stream-function-ized.rkt
#lang racket
(provide stream-cons/fn)
(require racket/stream)
(define (stream-cons/fn a b)
(stream-cons (a) (b)))
; typed-stream.rkt
#lang typed/racket
(provide stream-cons)
(require/typed "untyped-stream-function-ized.rkt"
[stream-cons/fn (All (a) (-> (-> a) (-> (Sequenceof a)) (Sequenceof a)))])
(define-simple-macro (stream-cons a b)
(stream-cons/fn (thunk a) (thunk b)))
This is the strategy I used for my `typed-racket-stream` repository:
I wanted to do this instead of "rolling my own" stream library because I wanted to be able to share the same stream data-structure as existing untyped racket files.
Alex Knauth