On this page:
1.1 Reactive API Reference
u/  a-build-system?
live-build?
start-live-build!
make-u/  a-build-system
1.2 Reactive Assets as Modules
make-asset
asset?
asset
make-asset-contract
asset/  c
make-u/  a-procure-procedure
1.3 Example:   Living Values based on Files
7.7

1 Reactive Model

 (require unlike-assets/reactive)
  package: unlike-assets-lib

The reactive model uses my kinda-ferpy library to track dependencies between assets and build them both lazily and asynchronously. This model is best for those who prefer functional programming and don’t want to deal with the underlying graph.

This module is experimental. Use the imperative model if you need a stable interface. Once this stabilizes, I suggest you prefer it over the imperative model.

1.1 Reactive API Reference

procedure

(u/a-build-system? p)  boolean?

  p : any/c
Returns #t if p is an unlike asset build system created by make-u/a-build-system.

procedure

(live-build? p)  boolean?

  p : any/c
Returns #t if p is a live build created by start-live-build!.

procedure

(start-live-build! key    
  #:sample! sample!    
  #:build! build!    
  [#:suppress? suppress?])  live-build?
  key : string?
  sample! : (-> any/c)
  build! : (-> any/c any/c)
  suppress? : (-> any/c any/c any/c?) = eq?
Returns a procedure LB that encapsulates a value that changes due to external factors.

When you apply start-live-build!, you also apply sample!, then build!. build! will run on its own thread.

Specifically, this procedure creates two stateful cells:

Each time you apply LB, it will apply sample! and compare the returned value to %S using suppress?. If suppress? returns a false value, then %S will update to the new value and cause %B to update.

(LB) will return %B. You can depend on this cell to monitor when the build thread restarts.

Why have stop? at all? Because (%B) returns a procedure that waits for the build thread to finish. This means ((%B)) waits for the build output. If that build output is a procedure–which is normal when resolving dependencies–then getting to the final value means writing a comical expression with at least three bracket pairs: (((%B))). stop? allows you to search for a common representation of build output.

(LB stop?) will apply (stop? %B), then (stop? (%B)), (stop? ((%B))), and so on until it encounters a true result. The value V that made (stop? V) true value is the value returned. This implies that (LB) is equivalent to (LB stateful-cell?).

procedure

(make-u/a-build-system key->live-build    
  [known])  u/a-build-system?
  key->live-build : (-> any/c u/a-build-system? live-build?)
  known : (and/c hash? (not/c immutable?)) = (make-hash)
Returns a procedure S that encapsulates known, a mutable hash of live builds encountered through use of S.

(S) evaluates to known.

(S key) will return a live build with the given key. If the build does not already exist in known, then it will first be added using (key->live-build key S). Subsequent applications of S to key will return the same reference to the live build without consulting key->live-build. Notice that S can be invoked recursively in the body of key->live-build. This allows one live build to depend on others. Be warned that if a cycle forms, then the build will not terminate.

(S key stop?) behaves like (S key), but will also apply the live build procedure LB to stop? (See start-live-build!).

(S key stop? make-alias) is useful for clients that want to refer to a live build by the name of its product. If an asset "main.css.rkt" produces "f871a234.css", then (S "f871a234.css") will return the same live build that produced it. Note that this does NOT return the same version of the asset the name implies! It returns the living build and therefore only the latest version, even if the name is outdated.

(S key stop? make-alias) behaves like (S key stop?), with the added behavior of creating an alias for key based on the produced value. make-alias is a procedure that accepts two formals and returns an alias for key. The arguments are always key and (S key stop?), in that order. If make-alias produces a key that already exists, that value for that key will be overwritten. Once evaluated, (eq? (S key) (S (make-alias key (S key stop?)))) is true.

1.2 Reactive Assets as Modules

Under the reactive model, assets are living hashes that act as restricted "modules". When combined with make-u/a-build-system, you can make procure procedures. Procure procedures load non-Racket resources as if they were dynamic Racket modules with reload support enabled.

procedure

(make-asset h)  procedure?

  h : (and/c immutable? hash?)
Returns a procedure P such that:

procedure

(asset? v)  boolean?

  v : any/c
Returns #t if v is a value produced by make-asset.

syntax

(asset pair ...)

 
pair = [id expr]
A macro that expands to a make-asset call.

(asset [media-type #"text/html"]
       [version '(1 3)])

procedure

(make-asset-contract 
  #:allow-missing-keys? allow-missing-keys? 
  pair) 
  flat-contract?
  allow-missing-keys? : any/c
  pair : (cons/c symbol? flat-contract?)
Returns a contract that ensures a given asset’s values match their own contracts.

If allow-missing-keys? is a true value, then the contract will not break if a key is not defined in the underlying asset’s hash.

syntax

(asset/c pair ... maybe-optional-pairs)

 
pair = [id contract-expr]
     
maybe-optional-pairs = 
  | #:optional
  | pair
  | ...
The macro form of make-asset-contract. The following two expressions are equivalent:

(or/c (make-asset-contract #:weak? #f (list (cons 'media-type bytes?)
                                            (cons 'writer (-> output-port? any))))
      (make-asset-contract #:weak? #t (list (cons 'alias string?))))
(asset/c [media-type bytes?]
         [writer (-> output-port? any)]
         #:optional
         [alias string?])

procedure

(make-u/a-procure-procedure S)  (->* (string?) #:rest symbol?)

  S : u/a-build-system?
Returns a procure procedure P that behaves like a dynamic module resolver for results matching (S key asset?).

; Strong procurement
(define html-formatted-markdown (P "index.md" 'html))
 
; Weak procurement
(define build-cell (P "index.md"))
 
; Srong procurement with multiple values.
(define-values (html-formatted-markdown output-file) (P "index.md" 'html 'out-file))

1.3 Example: Living Values based on Files

(require racket/runtime-path)
 
; Base change detection on file modification seconds.
(define (start-live-file-build! key build!)
  (start-live-build! key
    #:sample!   (λ () (file-or-directory-modify-seconds key))
    #:suppress? =
    #:build!    build!))
 
(define (start-live-line-count key u/a)
  (start-live-file-build! key
                          (λ (mtime) (length (file->lines key)))))
 
(define (start-live-mtime key u/a)
  (start-live-file-build! key identity))
 
(define (key->live-build key u/a)
  (define start!
    (if (string-suffix? key ".txt")
        start-live-line-count
        start-live-mtime))
  (start! key u/a))
 
(define sys (make-u/a-build-system key->live-build))
 
; This will return the current modification time of dummy.bin.
(sys "dummy.bin" number?)
; This will return the current number of lines in passage.txt.
(sys "passage.txt" number?)