<div dir="ltr">OC could suggest moving heavily called functions across boundaries, that'd be cool.<div><br></div><div style>One other place that we realized pretty quickly where we were wrong about the boundaries and inefficiencies is struct declarations. It isn't uncommon to put contracts on structs and put them in their own module (scribble does this). Currently the contract system can't tell that a struct contract is actually on a struct and thus avoid checking when doing a selection, but maybe that's somewhere that could help.</div>
<div style><br></div><div style>Robby</div></div><div class="gmail_extra"><br><br><div class="gmail_quote">On Thu, Dec 27, 2012 at 5:32 PM, Matthias Felleisen <span dir="ltr"><<a href="mailto:matthias@ccs.neu.edu" target="_blank">matthias@ccs.neu.edu</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><br>
1. Robby responded to your first item some time back. He and I had discussed contract-stronger and eq? check several times in the past. But even that doesn't assure improved checking w/o some performance-oriented thinking on the programmer's side.<br>
<br>
2. Sam didn't respond to the second part, which contained two questions. To answer the first one, I changed your code a bit. As for the second one, I think it is a lack os smarts but I am not sure this smarts can be exploited. See below, with modifications labeled MF.<br>
<br>
3. Because we anticipated performance distortions by contracts, Robby and I decided to make contracts a 'module' level tool. The hope was that Racket programmers would (1) trust themselves with their own code within a module ("No panic on the Titanic" was my slogan to remind ourselves of this argument; why choose an untyped language otherwise?) and (2) avoid 'tight' loops across module boundaries.<br>
<br>
Note: Since then we have learned that Racket programmers don't trust themselves; see the introduction of define/contract and friends and the repeated misunderstanding that this would check contracts even for internal recursive calls. We have also learned that in the context of generated contracts -- TR -- tight loops might show up.<br>
<br>
I am beginning to wonder whether the work on Optimization Coach should turn to this area next i.e. whether we should figure out a tool that anticipates potential performance problems in linked programs. As a stand-alone library racket/math clearly doesn't pose any problems. When linked into an untyped context, things might go wrong however -- as your toy benchmark shows. Or we just sit back and hope that nobody is ever bothered by this performance hit because 'realistic' programs won't suffer from this problem.<br>
<br>
-- Matthias<br>
<div class="im"><br>
<br>
<br>
#lang racket<br>
<br>
;; Provides a predicate and constructor for the opaque type `Foo'<br>
(module foo-defs racket<br>
</div> (provide foo? make-foo set-foos foos)<br>
<div class="im"><br>
(define (make-foo x) x)<br>
<br>
(define (foo? x)<br>
(printf "foo?~n")<br>
(exact-integer? x))<br>
<br>
</div> ;; MF: manipulate foos behind your back<br>
(define (set-foos v)<br>
(vector-set! v 5 (exact->inexact (vector-ref v 5))))<br>
<br>
;; MF: make foos here<br>
(define foos (build-vector 10 make-foo)))<br>
<br>
(module typed-defs typed/racket<br>
(provide get-foo foo-ap bar-ap)<br>
<div class="im"><br>
(require/typed<br>
(submod ".." foo-defs)<br>
[#:opaque Foo foo?]<br>
[make-foo (Integer -> Foo)]<br>
</div> ;; MF: type foos properly<br>
[foos (Vectorof Foo)]<br>
;; MF: promise the world here<br>
[set-foos ((Vectorof Foo) -> Void)])<br>
<div class="im"><br>
;; prints `foo?' 10 times; definitely necessary<br>
<br>
</div><div class="im"> (: get-foo (Integer -> Foo))<br>
(define (get-foo i)<br>
(vector-ref foos i))<br>
<br>
(: foo-ap (All (A) ((Foo -> A) Foo -> A)))<br>
(define (foo-ap f foo)<br>
(f foo))<br>
<br>
</div> (: bar-ap (All (A) ((Foo -> A) Integer -> A)))<br>
(define (bar-ap f foo)<br>
(f (get-foo foo))))<br>
<br>
(require 'typed-defs 'foo-defs)<br>
<br>
; I don't understand why the contract for `get-foo' has to check the return value, because TR already ensures that `get-foo' always returns a `Foo':<br>
<div class="im"><br>
(printf "going to get a foo~n")<br>
</div>(set-foos foos) ;; MF: this does NOT raise an error<br>
(with-handlers ((exn:fail:contract:blame? void))<br>
(get-foo 5) ; prints `foo?' once; why? MF: because it could have been modified, despite the type<br>
(displayln "ouch, it didn't catch the problem"))<br>
<br>
; Could TR generate (exact-integer? . -> . any) for `get-foo'?<br>
<div class="im"><br>
; Relatedly, TR already ensures that the function passed to `foo-ap' is only applied to `Foo' values, but this is also checked by a contract:<br>
<br>
(printf "going to apply a function to a foo~n")<br>
</div>(with-handlers ((exn:fail:contract:blame? void))<br>
(foo-ap identity (exact->inexact (get-foo 1)))) ; prints `foo?' twice; why not once, just for 1?<br>
;; MF: the first time because it might get a bad foo<br>
<br>
<br>
(bar-ap identity 5) ; prints `foo?' twice; why not once, just for 1?<br>
;; MF: After some playing around, I don't know either.<br>
;; Conjecture: the translation of the All type does not take into account that no matter what (Foo -> A) is applied to a Foo<br>
;; -- statically typed for sure, and possibly protected with a contract<br>
<div class="HOEnZb"><div class="h5"><br>
<br>
_________________________<br>
Racket Developers list:<br>
<a href="http://lists.racket-lang.org/dev" target="_blank">http://lists.racket-lang.org/dev</a><br>
</div></div></blockquote></div><br></div>