Here's the "quick" way I'd write the real-sqrt in Racket, combined with an illustration of one of the advantages of using the established contract combinators: here they gracefully report a kind of error that in the original would have caused an error in the error-checking code.
(define/contract (real-sqrt x) (->i ([x (>=/c 0)]) [rslt (x) (if (= 0 x) (=/c 0) (and/c (>=/c 0) (λ (rslt) (<= (abs (- x (* rslt rslt))) error))))]) "not even a number") The part of what you describe that has the least support in the Racket system is controlling the level of safety through a mechanism like pre-conditions-on/post-conditions-on. It would be easy enough to create a contract that is always satisfied if e.g. pre-conditions-on (which might be most Rackety to right as a parameter) is non-false, but I would suspect, at least in a case like this, you would already have paid most of the cost by getting into the contract system in the first place. The most typical solution, as Matthew illustrates, is to attach the contract at a module boundary with contract-out rather than a function boundary with define/contract, perhaps ultimately to attach it only to the public API of your library rather than for internals, and to leave off checks on the domains of functions (with the "any" contract) once you know they behave properly. Personally, though, having been annoyed once too often in tracking down an error that turned out to result from having changed the number of values a function returned, I'm happy to pay the price for good errors and trust the wonderful Racket implementers to keep the price as cheap as possible. Interestingly I had some trouble getting the nicely specific error messages you had in your example: I'll post about that separately. -Philip On Tue, May 2, 2017 at 9:22 PM, Matthew Butterick <m...@mbtype.com> wrote: > > On May 2, 2017, at 4:33 PM, Daniel Prager <daniel.a.pra...@gmail.com> > wrote: > > (define/pre-post (real-sqrt x) > #:pre ([(real? x) "real argument expected"] > [(>= x 0) "non-negative argument expected"]) > > #:implementation (sqrt x) > > #:post ([(implies (= x 0) (= result 0)) "The sqrt of zero should be > zero"] > [(implies (> x 0) (> result 0)) "Positive numbers have positive > square-roots"] > [(implies (> x 0) (<= (abs (- x (* result result))) 0.0000001)) > "result * result = x (to within error)"])) > > > > It sounds like you want alternate notation for stuff the contract system > can already do. > > AFAICT the function below is equivalent to your function above. One could > create a macro to go from one to the other: > > > (define/contract (real-sqrt x) > (([x (and/c > (or/c (λ (x) (real? x)) > (curry raise-argument-error 'real-sqrt "real argument > expected")) > (or/c (λ (x) (>= x 0)) > (curry raise-argument-error 'real-sqrt "non-negative > argument expected")))]) > . ->i . > [result (x) > (cond > [(= x 0) (or/c (λ (result) (= result 0)) > (curry raise-result-error 'real-sqrt "sqrt of > zero should be zero"))] > [(> x 0) (and/c > (or/c (λ (result) (> result 0)) > (curry raise-result-error 'real-sqrt > "sqrt of positive number should be positive")) > (or/c (λ (result) (<= (abs (- x (* result > result))) 0.00000001)) > (curry raise-result-error 'real-sqrt > "result * result = x (to within error)")))])]) > (sqrt x)) > > > Thinking more about the functional context, a macro — say (define/pre-post > ...) that cleanly defined the following functions would be pretty sweet > (continuing with the real-sqrt example): > > - real-sqrt-unsafe > - real-sqrt-with-pre-conditions > - real-sqrt-with-pre-and-post-conditions > - real-sqrt (make-parameter one-of-the above) > > > Rather than mint new identifiers, one could also export the function via > multiple submodules, each of which has a different level of contracting. > The advantage is that the "safety level" 1) can be adjusted by the caller, > 2) without having to change the name of the identifier in the code. Again, > this could all be automated through a macro: > > (begin > (define (real-sqrt x) > ;; no contract) > (provide real-sqrt) > > (module+ precheck > (provide (contract-out [real-sqrt precheck-contract ···]))) > > (module+ allchecks > (provide (contract-out [real-sqrt allcheck-contract ···]))) > ) > > And so on. > > > > -- > You received this message because you are subscribed to the Google Groups > "Racket Users" group. > To unsubscribe from this group and stop receiving emails from it, send an > email to racket-users+unsubscr...@googlegroups.com. > For more options, visit https://groups.google.com/d/optout. > -- You received this message because you are subscribed to the Google Groups "Racket Users" group. To unsubscribe from this group and stop receiving emails from it, send an email to racket-users+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.