Skip to content

Instantly share code, notes, and snippets.

@takikawa
Last active August 29, 2015 14:06
Show Gist options
  • Save takikawa/e2adef8dfd409fe88d52 to your computer and use it in GitHub Desktop.
Save takikawa/e2adef8dfd409fe88d52 to your computer and use it in GitHub Desktop.
optimizable to a single wrapper with B+, B-
(box/c int?)
|----------------| A+ B- |---------------|
| >-| ---------> | b' |
| | | | |
| A | | | B |
| | | | |
| | | | |
| ^-| <--------- | b = (box 3) |
|----------------| A- B+ |---------------|
(box/c int?)
References to the box in A will always use the version wrapped with the
original contract. That is, (mon (box/c int?) b B A).
Therefore writes to b in A will check int? and blame A on failure. Reads
from b in A will check int? and blame B on failure.
This is still ok even if the contract is (box/c (-> int? int?)) because
a write will wrap the function with (-> B A) checking (blame A on failure
of domain contract, blame B on failure of range). Reads will check
(-> A B) as expected.
Now b' would normally be (mon (box/c int?) (mon (box/c int?) b B A) A B).
But this is equivalent to (mon (box/c int?) b B B).
That's because writes to (mon (box/c int?) (mon (box/c int?) b B A) A B) will
first check int? and blame B on failure and then check int? and blame A on failure
if the first check succeeded. Obviously it is impossible for the contract to
blame A though because the int? check is identical.
Similarly, a read will check int? and blame B on failure and then check again
blaming A on failure. By the same argument as above, A can't be blamed.
;;;
Now, finally (mon (box/c int?) b B B) is the same as not attaching a contract
at all in Typed Racket because we know that TR can never fail to meet either the
positive or negative obligations of the type/contract anyway.
Or in other words, A can't possibly distinguish whether TR is using the original
reference or the new contract-wrapped one anyway, so it doesn't matter if we make them
exactly the same.
TR only: optimizable to no contract
(box/c int?)
|----------------| A+ B- |---------------|
| >-| ---------> | b' |
| | | | |
| untyped A | | | typed B |
| | | | |
| | | | |
| ^-| <--------- | b = (box 3) |
|----------------| A- B+ |---------------|
(box/c int?)
@sstrickl
Copy link

sstrickl commented Sep 9, 2014

If you only wrap once, what happens with the following code? Who gets blamed for the call with "foo"?

(module A racket
  (require B)
  (f b)
  ((unbox b) "foo"))

(module B racket/typed

  (: b (Box (-> Int Int)))
  (define b (box (lambda (x) x)))
  (provide b)

  (: f (-> (Box (-> Int Int)) Int))
  (define f (box-set! (lambda (x) (+ x 1))))
  (provide f))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment