Skip to content

Commit 8283a09

Browse files
committed
implement opaque-positive-predicate
1 parent 365807c commit 8283a09

File tree

5 files changed

+114
-21
lines changed

5 files changed

+114
-21
lines changed

typed-racket-doc/typed-racket/scribblings/reference/special-forms.scrbl

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,10 +496,25 @@ the type:
496496

497497
Evaluates to a predicate for the type @racket[t], with the type
498498
@racket[(Any -> Boolean : t)]. @racket[t] may not contain function types, or
499-
types that may refer to mutable data such as @racket[(Vectorof Integer)].}
499+
types that may refer to mutable data such as @racket[(Vectorof Integer)].
500+
501+
If @racket[t] contains any @racket[Opaque] types, @racket[make-predicate] will
502+
produce a warning. You should use @racket[make-positive-predicate] instead.}
500503

501504
@defform[(define-predicate name t)]{
502-
Equivalent to @racket[(define name (make-predicate t))].
505+
Equivalent to @racket[(define name (make-predicate t))].}
506+
507+
@defform[(make-positive-predicate t)]{
508+
509+
Evaluates to a predicate for the type @racket[t], with the type
510+
@racket[(Any -> Boolean : #:+ t)]. @racket[t] may not contain function types, or
511+
types that may refer to mutable data such as @racket[(Vectorof Integer)].
512+
513+
@racket[make-positive-predicate] does work with @racket[Opaque] types
514+
without any warnings.}
515+
516+
@defform[(define-positive-predicate name t)]{
517+
Equivalent to @racket[(define name (make-positive-predicate t))].}
503518

504519
@section{Type Annotation and Instantiation}
505520

typed-racket-lib/typed-racket/base-env/prims-contract.rkt

Lines changed: 57 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,28 @@
1212
;;
1313
;; - their implementations (under the same names) are defined at phase
1414
;; 0 using `define` in the main module
15-
;;
15+
;;
1616
;; - the `forms` submodule uses `lazy-require` to load the
1717
;; implementations of the forms
1818

1919

2020
(provide require/opaque-type require-typed-struct-legacy require-typed-struct
2121
require/typed-legacy require/typed require/typed/provide
2222
require-typed-struct/provide core-cast make-predicate define-predicate
23+
make-positive-predicate define-positive-predicate
2324
require-typed-signature)
2425

2526
(module forms racket/base
2627
(require (for-syntax racket/lazy-require racket/base))
27-
(begin-for-syntax
28+
(begin-for-syntax
2829
(lazy-require [(submod "..")
2930
(require/opaque-type
3031
require-typed-signature
3132
require-typed-struct-legacy
3233
require-typed-struct
3334
require/typed-legacy require/typed require/typed/provide
34-
require-typed-struct/provide core-cast make-predicate define-predicate)]))
35+
require-typed-struct/provide core-cast make-predicate define-predicate
36+
make-positive-predicate define-positive-predicate)]))
3537
(define-syntax (def stx)
3638
(syntax-case stx ()
3739
[(_ id ...)
@@ -43,7 +45,8 @@
4345
require-typed-struct-legacy
4446
require-typed-struct
4547
require/typed-legacy require/typed require/typed/provide
46-
require-typed-struct/provide make-predicate define-predicate)
48+
require-typed-struct/provide make-predicate define-predicate
49+
make-positive-predicate define-positive-predicate)
4750

4851
;; Expand `cast` to a `core-cast` with an extra `#%expression` in order
4952
;; to prevent the contract generation pass from executing too early
@@ -204,7 +207,7 @@
204207
;; define `cnt*` to be fixed up later by the module type-checking
205208
(define cnt*
206209
(syntax-local-lift-expression
207-
(make-contract-def-rhs #'ty #f (attribute parent))))
210+
(make-contract-def-rhs #'ty #f #f (attribute parent))))
208211
(quasisyntax/loc stx
209212
(begin
210213
;; register the identifier so that it has a binding (for top-level)
@@ -269,12 +272,18 @@
269272
;; Conversion of types to contracts
270273
;; define-predicate
271274
;; make-predicate
275+
;; define-positive-predicate
276+
;; make-positive-predicate
272277
;; cast
273278

274279
;; Helpers to construct syntax for contract definitions
275-
;; make-contract-def-rhs : Type-Stx Boolean Boolean -> Syntax
276-
(define (make-contract-def-rhs type flat? maker?)
277-
(define contract-def `#s(contract-def ,type ,flat? ,maker? untyped))
280+
;; make-contract-def-rhs : Type-Stx Boolean Boolean Boolean -> Syntax
281+
;; The exact? argument determines whether the contract must decide
282+
;; exactly whether the value has the type.
283+
;; - flat? true and exact? true must generate (-> Any Boolean : type)
284+
;; - flat? true and exact? false can generate (-> Any Boolean : #:+ type)
285+
(define (make-contract-def-rhs type flat? exact? maker?)
286+
(define contract-def `#s(contract-def ,type ,flat? ,exact? ,maker? untyped))
278287
(contract-def-property #'#f (λ () contract-def)))
279288

280289
;; make-contract-def-rhs/from-typed : Id Boolean Boolean -> Syntax
@@ -291,7 +300,7 @@
291300
(if types
292301
#`(U #,@types)
293302
#f)))
294-
`#s(contract-def ,type-stx ,flat? ,maker? typed))))
303+
`#s(contract-def ,type-stx ,flat? #f ,maker? typed))))
295304

296305

297306
(define (define-predicate stx)
@@ -312,8 +321,9 @@
312321
(define (make-predicate stx)
313322
(syntax-parse stx
314323
[(_ ty:expr)
324+
; passing #t for exact? makes it produce a warning on Opaque types
315325
(define name (syntax-local-lift-expression
316-
(make-contract-def-rhs #'ty #t #f)))
326+
(make-contract-def-rhs #'ty #t #t #f)))
317327
(define (check-valid-type _)
318328
(define type (parse-type #'ty))
319329
(define vars (fv type))
@@ -325,6 +335,41 @@
325335
#`(#,(external-check-property #'#%expression check-valid-type)
326336
#,(ignore-some/expr #`(flat-contract-predicate #,name) #'(Any -> Boolean : ty)))]))
327337

338+
339+
(define (define-positive-predicate stx)
340+
(syntax-parse stx
341+
[(_ name:id ty:expr)
342+
#`(begin
343+
;; We want the value bound to name to have a nice object name. Using the built in mechanism
344+
;; of define has better performance than procedure-rename.
345+
#,(ignore
346+
(syntax/loc stx
347+
(define name
348+
(let ([pred (make-positive-predicate ty)])
349+
(lambda (x) (pred x))))))
350+
;; not a require, this is just the unchecked declaration syntax
351+
#,(internal (syntax/loc stx (require/typed-internal name (Any -> Boolean : #:+ ty)))))]))
352+
353+
354+
(define (make-positive-predicate stx)
355+
(syntax-parse stx
356+
[(_ ty:expr)
357+
; passing #f for exact? makes it work with Opaque types without warning
358+
(define name (syntax-local-lift-expression
359+
(make-contract-def-rhs #'ty #t #f #f)))
360+
(define (check-valid-type _)
361+
(define type (parse-type #'ty))
362+
(define vars (fv type))
363+
;; If there was an error don't create another one
364+
(unless (or (Error? type) (null? vars))
365+
(tc-error/delayed
366+
"Type ~a could not be converted to a predicate because it contains free variables."
367+
type)))
368+
#`(#,(external-check-property #'#%expression check-valid-type)
369+
#,(ignore-some/expr #`(flat-contract-predicate #,name) #'(Any -> Boolean : #:+ ty)))]))
370+
371+
372+
328373
;; wrapped above in the `forms` submodule
329374
(define (core-cast stx)
330375
(syntax-parse stx
@@ -349,7 +394,7 @@
349394
#'v]
350395
[else
351396
(define new-ty-ctc (syntax-local-lift-expression
352-
(make-contract-def-rhs #'ty #f #f)))
397+
(make-contract-def-rhs #'ty #f #f #f)))
353398
(define existing-ty-id new-ty-ctc)
354399
(define existing-ty-ctc (syntax-local-lift-expression
355400
(make-contract-def-rhs/from-typed existing-ty-id #f #f)))
@@ -397,7 +442,7 @@
397442
#,@(if (eq? (syntax-local-context) 'top-level)
398443
(list #'(define-syntaxes (hidden) (values)))
399444
null)
400-
#,(internal #'(require/typed-internal hidden (Any -> Boolean : (Opaque pred))))
445+
#,(internal #'(require/typed-internal hidden (Any -> Boolean : #:+ (Opaque pred))))
401446
#,(if (attribute ne)
402447
(internal (syntax/loc stx (define-type-alias-internal ty (Opaque pred))))
403448
(syntax/loc stx (define-type-alias ty (Opaque pred))))

typed-racket-lib/typed-racket/private/type-contract.rkt

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@
3030
(c:contract-out
3131
[type->static-contract
3232
(c:parametric->/c (a) ((Type? (c:-> #:reason (c:or/c #f string?) a))
33-
(#:typed-side boolean?) . c:->* . (c:or/c a static-contract?)))]))
33+
(#:typed-side boolean? #:exact? boolean?)
34+
. c:->* .
35+
(c:or/c a static-contract?)))]))
3436

3537
(provide change-contract-fixups
3638
change-provide-fixups
@@ -50,7 +52,11 @@
5052
#t)]
5153
[_ #f]))
5254

53-
(struct contract-def (type flat? maker? typed-side) #:prefab)
55+
;; The exact? field determines whether the contract must decide
56+
;; exactly whether the value has the type.
57+
;; - flat? true and exact? true must generate (-> Any Boolean : type)
58+
;; - flat? true and exact? false can generate (-> Any Boolean : #:+ type)
59+
(struct contract-def (type flat? exact? maker? typed-side) #:prefab)
5460

5561
;; get-contract-def-property : Syntax -> (U False Contract-Def)
5662
;; Checks if the given syntax needs to be fixed up for contract generation
@@ -83,7 +89,7 @@
8389
;; (such as mutually recursive class types).
8490
(define (generate-contract-def stx cache sc-cache)
8591
(define prop (get-contract-def-property stx))
86-
(match-define (contract-def type-stx flat? maker? typed-side) prop)
92+
(match-define (contract-def type-stx flat? exact? maker? typed-side) prop)
8793
(define *typ (if type-stx (parse-type type-stx) t:-Dead-Code))
8894
(define kind (if (and type-stx flat?) 'flat 'impersonator))
8995
(syntax-parse stx #:literals (define-values)
@@ -104,6 +110,7 @@
104110
;; unless it's used for with-type
105111
#:typed-side (from-typed? typed-side)
106112
#:kind kind
113+
#:exact? exact?
107114
#:cache cache
108115
#:sc-cache sc-cache
109116
(type->contract-fail
@@ -124,6 +131,7 @@
124131
(type->contract type
125132
#:typed-side #t
126133
#:kind 'impersonator
134+
#:exact? #f
127135
#:cache cache
128136
#:sc-cache sc-cache
129137
;; FIXME: get rid of this interface, make it functional
@@ -281,17 +289,18 @@
281289
[(both) 'both]))
282290

283291
;; type->contract : Type Procedure
284-
;; #:typed-side Boolean #:kind Symbol #:cache Hash
292+
;; #:typed-side Boolean #:kind Symbol #:exact? Boolean #:cache Hash
285293
;; -> (U Any (List (Listof Syntax) Syntax))
286294
(define (type->contract ty init-fail
287295
#:typed-side [typed-side #t]
288296
#:kind [kind 'impersonator]
297+
#:exact? [exact? #t]
289298
#:cache [cache (make-hash)]
290299
#:sc-cache [sc-cache (make-hash)])
291300
(let/ec escape
292301
(define (fail #:reason [reason #f]) (escape (init-fail #:reason reason)))
293302
(instantiate/optimize
294-
(type->static-contract ty #:typed-side typed-side fail
303+
(type->static-contract ty #:typed-side typed-side #:exact? exact? fail
295304
#:cache sc-cache)
296305
fail
297306
kind
@@ -338,6 +347,7 @@
338347

339348
(define (type->static-contract type init-fail
340349
#:typed-side [typed-side #t]
350+
#:exact? [exact? #t]
341351
#:cache [sc-cache (make-hash)])
342352
(let/ec return
343353
(define (fail #:reason reason) (return (init-fail #:reason reason)))
@@ -535,6 +545,10 @@
535545
[(Promise: t)
536546
(promise/sc (t->sc t))]
537547
[(Opaque: p?)
548+
(when exact?
549+
(eprintf
550+
"warning: Opaque predicates cannot be used as exact decisions yes/no:\n predicate: ~s\n"
551+
(syntax-e p?)))
538552
(flat/sc #`(flat-named-contract (quote #,(syntax-e p?)) #,p?))]
539553
[(Continuation-Mark-Keyof: t)
540554
(continuation-mark-key/sc (t->sc t))]

typed-racket-lib/typed-racket/private/with-types.rkt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
;; this is a parameter to avoid dependency issues
7474
[current-type-names
7575
(lazy
76-
(append
76+
(append
7777
(type-name-env-map (lambda (id ty)
7878
(cons (syntax-e id) ty)))
7979
(type-alias-env-map (lambda (id ty)
@@ -154,7 +154,7 @@
154154
(define (type-stxs->ids+defs type-stxs typed-side)
155155
(for/lists (_1 _2) ([t (in-list type-stxs)])
156156
(define ctc-id (generate-temporary))
157-
(define contract-def `#s(contract-def ,t #f #f ,typed-side))
157+
(define contract-def `#s(contract-def ,t #f #f #f ,typed-side))
158158
(values ctc-id
159159
#`(define-values (#,ctc-id)
160160
#,(contract-def-property
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#lang typed/racket
2+
3+
(module foo racket/base
4+
(provide foo?)
5+
(define x #false)
6+
(define (foo? v) (set! x (not x)) x))
7+
8+
(require/typed 'foo [#:opaque Foo foo?])
9+
;; foo? is a positive predicate: (-> Any Boolean : #:+ Foo)
10+
11+
(: no+ (∀ (T) (-> Any Boolean : #:+ T)))
12+
(define (no+ v) #false)
13+
14+
(: transmute (∀ (T) (-> Any T)))
15+
(define (transmute v)
16+
(if (and (or (foo? v) ((inst no+ T) v)) (not (foo? v)))
17+
v
18+
(error 'absurd)))
19+

0 commit comments

Comments
 (0)