typed-racket
typed-racket copied to clipboard
any-wrap: avoid wrapping immutable structs with immutable fields
When an immutable struct with immutable fields crosses an Any boundary, it shouldn't need a wrapper.
Example program (from #1332 ):
#lang typed/racket
(require/typed contract-profile
(contract-profile-thunk (-> (-> Any) Any)))
(define-type Fighter Integer)
(define-type Pill Integer)
(struct interactive [{whose-turn : Symbol} {state : State}] #:prefab #:type-name Interactive)
(struct state [{fighters : [Listof Fighter]} {pills : {Listof Pill}}] #:prefab #:type-name State)
(define mk-state
(let ((ff 0) (pp 0))
(lambda ()
(state
(begin0 (list ff) (set! ff (+ 1 ff)))
(begin0 (list pp) (set! pp (+ 1 pp)))))))
(define mk-interactive
(let ((ww 'who))
(lambda ()
(interactive
ww
(mk-state)))))
(define NN (expt 10 4)) ;; higher value, slower runtime
(define ii*
(for/list : (Listof Interactive) ((_kk : Natural (in-range NN)))
(cast (cast (mk-interactive) Any) Interactive)))
(contract-profile-thunk
(lambda ()
(for ((ii (in-list ii*)))
(define st (interactive-state ii))
(+ (first (state-fighters st))
(first (state-pills st))))))
The contract profile says 40% of the running time is contracts. But all the checks could happen outside the call to contract-profile-thunk.
For this to work, any-wrap.rkt will need to search through struct fields and the types inside.
(If Pill was a Boxof Integer, then the wrapper would be important.)
Note that (a) substructures could add mutable fields (and TR's types don't currently track prop:sealed) and (b) functions are "mutable" in this sense.
Understood. — Not unwrapping eagerly imposes a bad performance overhead (6x). We should try.
For any-wrap/c to avoid wrapping these, I don't see why TR's types not tracking prop:sealed should be relevant... any-wrap/c can use runtime reflection operations right?
The important thing would be making sure, at runtime, that a value known to be an instance of an immutable struct A, isn't actually an instance of a mutable struct B that inherits from A.
The 2nd return value of struct-info, skipped?, should be able to determine that, so it should only avoid wrapping it if skipped? is #false.