require/typed confusing contract optimization
bennn opened this issue · comments
The domains of functions imported via require/typed
are never checked at runtime, even if the functions are provided to untyped modules.
#lang racket/base
(module a racket/base
(provide f)
(define (f x) (add1 x)))
;; ----
(module b typed/racket/base
(require/typed/provide (submod ".." a)
[f (-> Natural Natural)]))
;; ----
(require 'b)
(f f)
This seems confusing at first, but I don't think it's actually unsound. Because this only lets you call untyped functions with the "wrong" type, and only in untyped contexts. If module a
is typed then it will actually get a contract from a
and the call will be checked.
Okay, I can change the title 😄
But I still think this is "weirdness" worth fixing for the case of an untyped script using a typed adaptor module.
(module typed-and-safe typed/racket/base
(require/typed racket/string ;; Some library I don't want to re-implement
[string-trim (-> String String)])
(provide (rename-out [string-trim safe-trim]))
...)
(module my-script racket/base
(require (submod ".." typed-and-safe))
(safe-trim 4) ;; But I trusted you to catch my mistakes!
)
Also odd is that this actually does the check in Racket v6.0. In v6.0.1 and v6.1 it doesn't.
@bennn and I discussed this again and we decided that we're not going to change anything here.
@bennn and I discussed this again and we decided that we're not going to change anything here.
👍
I was expecting module b
to take ownership of the function that passes through. TR doesn't do that, partly because the baseline Racket behavior for this require
/provide
pattern lets the outer module reference the function from a
without going through b
. (TR not taking ownership also means fewer contracts ~ faster performance.)
It's possible to get the behavior that I wanted with an extra define:
#lang racket/base
(module a racket/base
(provide f)
(define (f x) (add1 x)))
;; ----
(module b typed/racket/base
(require/typed (submod ".." a)
[(f -f) (-> Natural Natural)])
(define f -f)
(provide f))
;; ----
(require 'b)
(f f)
;;f: contract violation
;; expected: natural?
;; given: #<procedure:f>
;; in: the 1st argument of
;; (-> natural? any)
;; contract from:
;; (189.rkt b)
;; blaming: 189.rkt
;; (assuming the contract is correct)