diff --git a/info.rkt b/info.rkt index 2abc4ab..a312c1f 100644 --- a/info.rkt +++ b/info.rkt @@ -1,7 +1,7 @@ #lang info (define collection "with-cache") (define deps '("base" "typed-racket-lib")) -(define build-deps '("basedir" "scribble-lib" "racket-doc" "rackunit-lib" "pict-lib")) +(define build-deps '("basedir" "scribble-lib" "racket-doc" "rackunit-lib" "pict-lib" "typed-racket-doc")) (define pkg-desc "Simple, filesystem-based caching") (define version "0.6") (define pkg-authors '(ben)) diff --git a/private/with-cache.rkt b/private/with-cache.rkt index 1ca9c5b..f30379e 100644 --- a/private/with-cache.rkt +++ b/private/with-cache.rkt @@ -111,7 +111,7 @@ (let ([r (thunk)]) (when use? (define val-to-write - (with-handlers ([exn:fail? (λ (exn) (raise-user-error 'with-cache "Internal error: failed to make writable value from result '~e'" r))]) + (with-handlers ([exn:fail? (λ (exn) (raise-user-error 'with-cache "Internal error: failed to make writable value from result ~e and key-thunks ~e" r keys))]) (write-proc r))) (log-with-cache-info "writing cache file '~a'" cache-file) (with-handlers ([exn:fail? (cache-write-error cache-file)]) diff --git a/scribblings/with-cache.scrbl b/scribblings/with-cache.scrbl index 58574dc..8449347 100644 --- a/scribblings/with-cache.scrbl +++ b/scribblings/with-cache.scrbl @@ -234,3 +234,61 @@ The @racket[with-cache] function implements this pipeline and provides hooks for Logs @racket['info] events when reading or writing caches and @racket['error] events after detecting corrupted cache files. } +@section{Using Typed Racket} + +Using the @tt{with-cache} library with Typed Racket requires providing types for several functions. While these are mostly +straightforward, indiscriminate use of the @racket[Any] type can lead to some tricky errors. In particular, it's important +that Typed Racket knows which things are thunks, to ensure that they can safely be called on the @racket[with-cache] side. + +Here's a small example that works correctly: + +@codeblock|{ +#lang typed/racket + +(require/typed with-cache + [with-cache (Path-String (-> Any) -> Any)] + [cachefile (Path-String -> Path-String)] + [*current-cache-keys* ((Listof (-> Any)) -> Void)]) + +(define-type TestType (Immutable-HashTable Symbol Natural)) +(define-predicate test-type? TestType) + +(*current-cache-keys* + ;; cache is invalidated every second, for demonstration purposes: + (list (λ () (current-seconds)))) + +(define (compute-complex-value) : TestType + ;; imagine that this is hard to compute: + (hash 'a 1234)) + +(define cached : TestType + (assert + (with-cache (cachefile "complex-value-cache") + (λ () + (printf "refreshing cache\n") + (compute-complex-value))) + test-type?)) + +cached +}| + +Another amusing way to get into trouble with Typed Racket and with-cache is +importing the @racket[with-cache] function using a parameterized type, as in + +@code|{ +#lang typed/racket + + +(require/typed with-cache + ;; this does not work! + [with-cache (All (T) (Path-String (-> T) -> T))] + ...) + +... +}| + +This prevents with-cache from being able to inspect the values produced by +the thunk that is passed to it; if you want to specify a type for this thunk +(in order, for instance, to allow typed use of the result of with-cache +without a dynamic check) it's better simply to hard-code the type returned +by the thunk that your program uses. \ No newline at end of file