scramble:   Assorted Utility Libraries
1 About Descriptions
about
prop:  about
has-about?
2 Conditional Expressions
cond+
and+
or+
3 Synchronizable Events
make-box-evt
box-evt?
box-evt-set!
box-evt-ready?
4 Functions
K
K0
call
5 Immutable and Mutable Conversion
immutable
mutable
6 Lists
singleton?
7 Numbers
exact
inexact
min*
max*
ceiling-quotient
ceiling-multiple
floor-quotient
floor-multiple
8 Slices
slice
bytes-slice?
string-slice?
vector-slice?
print-slice-constructor-modes
slice-length
slice-contents
bytes-slice->string/  utf-8
string->bytes/  utf-8
9 Structs
prop:  auto-equal+  hash
10 Classes and Objects
init-private
constructor-style-printable<%>
get-printing-class-name
get-printing-components
print-quotable-never<%>
print-quotable-always<%>
about<%>
about
11 Compile-time Code Injection
begin/  inject-syntax
12 Compile-time Struct Info
adjust-struct-info
8.2

scramble: Assorted Utility Libraries

Ryan Culpepper <ryanc@racket-lang.org>

1 About Descriptions

 (require scramble/about) package: scramble-lib

The about interface is useful for displaying a partial description of a value in a situation where simply printing or writing the value would be inappropriate. See also about<%>.

procedure

(about v)  string?

  v : any/c
Returns a string describing v, if v implements the prop:about interface; otherwise, if (has-about? v) is false, then the string "(no description)" is returned.

Examples:
> (struct secret (bs)
    #:property prop:about
    (lambda (self) (format "~s-bit secret" (* 8 (bytes-length (secret-bs self))))))
> (define my-secret (secret #"hello"))
> (error 'secure-op "secret is too short\n  given: ~a" (about my-secret))

secure-op: secret is too short

  given: 40-bit secret

> (about 'apple)

"(no description)"

Property for structs to implement the about operation.

procedure

(has-about? v)  boolean?

  v : any/c
Returns #t if v is an instance of a struct type that implements the prop:about interface, #f otherwise.

2 Conditional Expressions

 (require scramble/cond) package: scramble-lib

syntax

(cond+ clause ... maybe-final-clause)

 
clause = [test-expr then-body ...+]
  | [test-expr => proc-expr]
  | #:do defn-or-expr
     
maybe-final-clause = 
  | [else then-body ...+]
  | #:else then-body ...+
Like cond, but allows addititional forms of clauses and raises an error if no clause is selected.

The additional forms of clause are allowed:
#:do defn-or-expr

If the preceding clauses were not selected, evaluate defn-or-expr before continuing. If defn-or-expr is a definition, the subsequent clauses are in its scope.

Separate #:do clauses have “let*” scoping. That is, each #:do clause’s form is evaluated in a separate internal definition context nested within the previous scopes. Use begin to group mutually recursive definitions.

#:else then-body ...+

Allowed only at the end. Equivalent to [else then-body ...].

Examples:
> (define entries
    `([x 5]
      [y ,(lambda (key) (list key 12))]))
> (define (lookup key)
    (define entry (assoc key entries))
    (cond+ [(not entry) (error "not found")]
           #:do (define v (cadr entry))
           [(procedure? v) (v key)]
           #:else v))
> (lookup 'x)

5

> (lookup 'y)

'(y 12)

> (lookup 'z)

not found

> (cond+ [(odd? 12) 'odd]
         [(even? 7) 'even-odder])

cond+: all clauses failed

  location: eval:7:0

syntax

(and+ part ... expr)

 
part = expr
  | #:do defn-or-expr

syntax

(or+ part ... expr)

 
part = expr
  | #:do defn-or-expr
Like and and or, respectively, but allowing #:do clauses among the expressions. The parts must be followed by a final expression—that is, unlike and and or, the arguments cannot be empty.

Example:
> (and+ (even? 42)
        #:do (define x (quotient 42 2))
        (even? x))

#f

3 Synchronizable Events

 (require scramble/evt) package: scramble-lib

procedure

(make-box-evt)  box-evt?

Returns a new box event. A box event becomes ready for synchronization when it is filled with a value by box-evt-set!, and its synchronization result is the value in the box. Once a box event becomes ready, it remains ready, and its contents cannot be changed.

Box events are thread-safe and break-safe but not kill-safe: If a thread is killed during a call to box-evt-set!, it is possible for the box event to become damaged—that is, unready for synchronization but also unable to be filled by another call to box-evt-set!.

Examples:
> (define bxe (make-box-evt))
> (sync/timeout 0 bxe)

#f

> (box-evt-set! bxe (list 1 2 3))

#t

> (sync/timeout 0 bxe)

'(1 2 3)

> (box-evt-set! bxe (list 7 8 9))

#f

> (sync/timeout 0 bxe)

'(1 2 3)

procedure

(box-evt? v)  boolean?

  v : any/c
Returns #t if v is a box event produced by make-box-evt, #f otherwise.

procedure

(box-evt-set! bxe v)  boolean?

  bxe : box-evt?
  v : any/c
If bxe is not ready, then fills it with v, causing bxe to become ready, and returns #t. If bxe is already ready, returns #f without changing bxe’s contents.

procedure

(box-evt-ready? bxe)  boolean?

  bxe : box-evt?
Returns #t if bxe is ready for synchronization, #f otherwise.

Equivalent to (sync/timeout 0 (wrap-evt bxe (lambda (v) #t))).

4 Functions

 (require scramble/function) package: scramble-lib

procedure

(K v ...)  procedure?

  v : any/c
Returns a constant function that produces (values v ...) when applied.

The resulting constant function’s arity is (arity-at-least 0); that is, it accepts (and discards) any number of positional arguments, but it does not accept keyword arguments. See const for a single-valued version whose result accepts keyword arguments.

Examples:
> (define always-zero (K 0))
> (map always-zero '(1 2 3))

'(0 0 0)

> (define two-falses (K #f #f))
> (define-values (head tail)
    (with-handlers ([exn? two-falses])
      (values (car '()) (cdr '()))))

procedure

(K0 v ...)  procedure?

  v : any/c
Like K, but the resulting constant function’s arity is 0 (with no keywords).

procedure

(call f v ...)  any

  f : procedure?
  v : any/c
Calls f on the arguments v ...; equivalent to (f v ...). Primarily useful as an argument to higher-order functions.

Examples:
> (define the-callbacks
    (list (lambda () (printf "here I am\n"))
          (lambda () (printf "me too!\n"))))
> (map call the-callbacks)

here I am

me too!

'(#<void> #<void>)

5 Immutable and Mutable Conversion

 (require scramble/immutable) package: scramble-lib

procedure

(immutable v)  any/c

  v : any/c
Returns a value like v that is shallowly immutable (see immutable?). The argument must be a vector, string, or other type of data with both mutable and immutable variants. (Mutable pairs are not considered a mutable variant of pairs.)

The result is immutable, and it is not an impersonator. If v is a suitable result, it is returned as the result; that is, the result of the function may not be a fresh value.

Note that if v is impersonated, this function can raise an exception due to accessing v.

procedure

(mutable v fresh?)  any/c

  v : any/c
  fresh? : boolean?
Like immutable, but returns a value that is shallowly mutable and not an impersonator.

If fresh? is true, then the result is a new value that does not share storage (shallowly) with v.

Note that if v is impersonated, this function can raise an exception due to accessing v.

6 Lists

 (require scramble/list) package: scramble-lib

procedure

(singleton? v)  boolean?

  v : any/c
Returns #t if v is a list of length 1; #f otherwise.

Equivalent to (and (pair? v) (null? (cdr v))).

Examples:
> (singleton? (list 'hello))

#t

> (singleton? (list 1 2 3))

#f

> (singleton? (shared ([whys (cons 'why whys)]) whys))

#f

7 Numbers

 (require scramble/number) package: scramble-lib

procedure

(exact r)  number?

  r : number?

procedure

(inexact r)  number?

  r : number?
Aliases for inexact->exact and exact->inexact, respectively.

procedure

(min* r ...)  real?

  r : real?

procedure

(max* r ...)  real?

  r : real?
Like min and max, respectively, but they return the appropriate infinity if given zero arguments, and comparisons against +inf.0 and -inf.0 do not coerce the result to inexact.

Examples:
> (min 1 +inf.0) ; normal min

1.0

> (min* 1 +inf.0)

1

> (min* 1 2.0)

1.0

> (max*)

-inf.0

Equivalent to (ceiling (/ n d)), but avoids the intermediate rational result.

Examples:
> (ceiling-quotient 7 4)

2

> (ceiling-quotient 8 4)

2

> (ceiling-quotient 9 4)

3

Returns the least multiple of d greater than or equal to n.

Equivalent to (* d (ceiling-quotient n d)).

Examples:
> (ceiling-multiple 7 4)

8

> (ceiling-multiple 8 4)

8

> (ceiling-multiple 9 4)

12

procedure

(floor-quotient n d)  exact-integer?

  n : exact-integer?
  d : exact-positive-integer?

procedure

(floor-multiple n d)  exact-integer?

  n : exact-integer?
  d : exact-positive-integer?
Equivalent to (floor (/ n d)) and (* d (floor-quotient n d)), respectively.

8 Slices

 (require scramble/slice) package: scramble-lib

struct

(struct slice (value start end))

  value : (or/c bytes? string? vector? slice?)
  start : exact-nonnegative-integer?
  end : exact-nonnegative-integer?
A slice represents a part of an underlying indexed collection (a byte string, string, or vector).

The start and end fields must be appropriate for value, and start must be less than or equal to end; otherwise, an exception is raised.

The slice constructor performs the following adjustments:
  • If end is #f, it is replaced with the length of the given value. That is slice-end never returns #f.

  • If value is a slice, then its value is extracted and start and end are adjusted to refer to the underlying value. That is, slice-value never returns a slice.

  • If start is equal to end, then value is replaced with the empty value of the same type, and start and end are set to 0.

See print-slice-constructor-modes for information about printing slices.

Note: Future versions of this library may extend the set of types allowed as values.

procedure

(bytes-slice? v)  boolean?

  v : any/c

procedure

(string-slice? v)  boolean?

  v : any/c

procedure

(vector-slice? v)  boolean?

  v : any/c
Returns #t if v is a slice containing a byte string, string, or vector, respectively; returns #f otherwise.

parameter

(print-slice-constructor-modes)  (listof (or/c #t #f 0 1))

(print-slice-constructor-modes modes)  void?
  modes : (listof (or/c #t #f 0 1))
 = '(0 1)
Determines whether a slice is printed as a struct or as its contents.

When a slices is printed using a mode (see gen:custom-write) in modes, it is printed as a struct; otherwise, only its contents are printed.

Examples:
> (define s (slice (for/vector ([i 16]) i) 9 13))
> (print s)

(slice '#(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) 9 13)

> (write s)

#(9 10 11 12)

> (parameterize ((print-slice-constructor-modes '(#t)))
    (write s))

#<slice: #(0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15) 9 13>

procedure

(slice-length s)  exact-nonnegative-integer?

  s : slice?
Returns the length of the slice.

Equivalent to (- (slice-end s) (slice-start s)).

procedure

(slice-contents s mutability)  (or/c bytes? string? vector?)

  s : slice?
  mutability : (or/c 'mutable 'immutable #f)
Returns a fresh copy of the slice’s contents.

If mutability is 'mutable, the result is mutable; if 'immutable, the result is immutable; otherwise, the result is immutable when the slice’s underlying value is immutable.

procedure

(bytes-slice->string/utf-8 bs [err-char])  string?

  bs : bytes-slice?
  err-char : (or/c char? #f) = #f

procedure

(string->bytes/utf-8 ss)  bytes?

  ss : string-slice?
Wrappers for bytes->string/utf-8 and string->bytes/utf-8, respectively, that obtain the value and indexes from the slice.

Example:
> (bytes-slice->string/utf-8 (slice #"hello world" 6 #f))

"world"

9 Structs

 (require scramble/struct) package: scramble-lib

Property that automatically adds an implementation of the prop:equal+hash property. If the property value is #t, the automatically generated equality predicate and hash code functions use all of the struct type’s fields. If the property value is a list, then the equality predicate and hash code functions use only the fields with the indexes in the list.

In addition to the indicated fields, the hash code function also depends on a random seed and the name of the struct type. A new random seed is generated for each instantiation of the scramble/struct module.

If prop:auto-equal+hash is attached to a struct type that has a super struct type, then the super struct type must also have the prop:auto-equal+hash, and the new equality and hash code functions extend the super type’s functions. If the super struct type does not have the prop:auto-equal+hash property, an error is raised.

> (struct point (x y)
    #:property prop:auto-equal+hash #t)
> (equal-hash-code (point 1 2))

911495098073887175

> (equal? (point 1 2) (point 1 2))

#t

> (equal? (point 1 2) (point 0 0))

#f

In the following example, the equality and hash code functions of the point3 struct type use only the z field out of point3’s fields, disregarding the color field, but they also use both of point’s fields.

> (struct point3 point (z color)
    #:property prop:auto-equal+hash (list (struct-field-index z)))
> (equal? (point3 1 2 3 #f)
          (point3 1 2 3 'red))

#t

> (equal? (point3 0 0 3 'red)
          (point3 1 2 3 'red))

#f

> (equal? (equal-hash-code (point3 1 2 3 #f))
          (equal-hash-code (point3 1 2 3 'red)))

#t

10 Classes and Objects

 (require scramble/class) package: scramble-lib

syntax

(init-private init-decl ...)

Like init-field, but declares private fields instead of public fields. That is, the fields are declared via define rather than field; the corresponding init-argument names are still externally visible.

Examples:
> (define person%
    (class object%
      (init-field name)
      (init-private [id #f])
      (super-new)))
> (define alice (new person% (name "Alice") (id 1001)))
> (get-field name alice)

"Alice"

> (get-field id alice)

get-field: given object does not have the requested field

  field name: id

  object: (object:person% ...)

Interface for objects that implement constructor-style printing—that is, as a new expression with the class name and a sequence of initialization arguments (init arguments).
If the object’s state is not representable in terms of its actual init arguments, then consider using printable<%> to implement a printer that does not produce constructor-style output. Or alternatively, also implement the (empty) interface print-quotable-always<%> to force printing in non-expression style.
See also prop:custom-write and make-constructor-style-printer.

method

(send a-constructor-style-printable get-printing-class-name)

  symbol?
Returns a symbol to be used as the class name when printed.

method

(send a-constructor-style-printable get-printing-components)

  
(listof symbol?) (listof any/c) boolean?
Returns (values names values more?), which control the printing of the object’s contents as follows:

The names represent the names of the object’s fields—or more properly, its init arguments. The values are the corresponding values; the two lists should have the same length.

If more? is true, then ... is printed after the initialization arguments to indicate that the object contains additional state not represented by the printed output.

Examples:
> (define friendly-person%
    (class* person% (constructor-style-printable<%>)
      (inherit-field name)
      (super-new)
  
      (define/public (get-printing-class-name)
        'friendly-person%)
      (define/public (get-printing-components)
        (values '(name) (list name) #t))))
> (define bob (new friendly-person% (name "Bob")))
> bob

(new friendly-person% (name "Bob") ...)

> (list bob)

'(#<friendly-person%: (name "Bob") ...>)

> (write bob)

#<friendly-person%: (name "Bob") ...>

This interface contains no methods. It attaches the prop:custom-print-quotable property to objects, with the value 'never.

Examples:
> (define expressive-person%
    (class* friendly-person% (print-quotable-never<%>)
      (super-new)
      (define/override (get-printing-class-name)
        'expressive-person%)))
> (define kristen (new expressive-person% (name "Kristen")))
> kristen

(new expressive-person% (name "Kristen") ...)

> (list kristen)

(list (new expressive-person% (name "Kristen") ...))

> (write kristen)

#<expressive-person%: (name "Kristen") ...>

This interface contains no methods. It attaches the prop:custom-print-quotable property to objects, with the value 'always.

Examples:
> (define obscure-person%
    (class* friendly-person% (print-quotable-always<%>)
      (super-new)
      (define/override (get-printing-class-name)
        'obscure-person%)))
> (define jeff (new obscure-person% (name "Jeff")))
> jeff

'#<obscure-person%: (name "Jeff") ...>

> (list jeff)

'(#<obscure-person%: (name "Jeff") ...>)

> (write jeff)

#<obscure-person%: (name "Jeff") ...>

interface

about<%> : interface?

This interface attaches the prop:about property to objects with an implementation that calls the about method.

method

(send an-about about)  string?

Implements the about operation.

11 Compile-time Code Injection

 (require scramble/inject-syntax) package: scramble-lib

syntax

(begin/inject-syntax body ...+)

Evaluates the body forms at compile time. The bodys must end in an expression that produces a syntax object; otherwise, a syntax error is raised. The syntax object result replaces the begin/inject-syntax form. In other words, a begin/inject-syntax expression is similar to an immediate application of an anonymous macro, except that no macro scope is added to the resulting syntax.

Any side-effects performed by the bodys occur only once, when the begin/inject-syntax form is compiled. This is in contrast to begin-for-syntax, for example, whose contents are also evaluated whenever the enclosing module is visited.

If begin/inject-syntax is used in an expression context, the resulting syntax object must be an expression form; otherwise, the macro expander will raise a syntax error.

One use of begin/inject-syntax is conditional compilation: such as conditional definitions or module imports. For example, the following code either requires a module or defines a compatibility function depending on the current version:
> (require (for-syntax racket/base) scramble/inject-syntax)
> (begin-for-syntax
    (define (version-less-than? v) .... (version) ....))
> (begin/inject-syntax
    (if (version-less-than? "7.6") ; racket/symbol was added in 7.6
        #'(begin (define (symbol->immutable-string s)
                   (string->immutable-string (symbol->string s))))
        #'(begin (require (only-in racket/symbol symbol->immutable-string)))))
> (symbol->immutable-string 'hello)

"hello"

The following example checks whether an identifier is currently defined (useful, for example, when a new name is added to an existing module): defined:
> (require racket/string)
> (begin/inject-syntax
    (if (identifier-binding #'string-exclaim) ; Already defined? (No)
        #'(begin)
        #'(define (string-exclaim str)
            (regexp-replace* #rx"[.]" str "!"))))
> (string-exclaim "Thanks. Have a nice day.")

"Thanks! Have a nice day!"

The following example selects between different implementations (eg, safe vs unsafe or with contracts vs without contracts) based on a compile-time configuration variable:
> (require racket/require)
> (define-for-syntax use-safe-fx-ops? #t)
> (begin/inject-syntax
    (if use-safe-fx-ops?
        #'(require (prefix-in unsafe- racket/fixnum))
        #'(require (matching-identifiers-in #rx"^unsafe-fx" racket/unsafe/ops))))
> (unsafe-fx+ 1 2)

3

Keep in mind that it is customary in Racket to do library configuration at run time via ordinary variables, parameters, etc. Prefer run-time mechanisms when possible. Use begin/inject-syntax when compile-time concerns are involved, such as scoping and variations in module exports.

Another use is to perform compile-time computations of quotable data:
> (begin-for-syntax
    (define (slow-popcount n)
      (cond [(zero? n) 0]
            [else (+ (slow-popcount (quotient n 2))
                     (if (bitwise-bit-set? n 0) 1 0))])))
> (define (faster-popcount n)
    (define (byte-popcount b)
      (bytes-ref (begin/inject-syntax
                   #`(quote #,(apply bytes (for/list ([n 256]) (slow-popcount n)))))
                 b))
    (cond [(zero? n) 0]
          [else (+ (faster-popcount (arithmetic-shift n -8))
                   (byte-popcount (bitwise-bit-field n 0 8)))]))
> (faster-popcount #xFFFFFFFF)

32

Warning: Code can be run on a different platform from the one it was compiled on. Don’t use compile-time conditions to specialize code based on features that may change between compile time and run time. On Racket BC, these include the size of fixnums, the operating system, path conventions, and so on. On Racket CS, these features might not change, but beware of cross-compilation.

12 Compile-time Struct Info

 (require scramble/struct-info) package: scramble-lib

procedure

(adjust-struct-info base-info 
  [#:constructor constructor-id 
  #:match-expander match-expander]) 
  struct-info?
  base-info : struct-info?
  constructor-id : (or/c identifier? #f) = #f
  match-expander : (or/c #f (-> syntax? syntax?)) = #f
Produces a struct-info record like base-info but with new behavior as a constructor and match pattern. That is, if name is bound to the result using define-syntax etc, and if constructor-id is not #f, then a use of name as an expression is redirected to constructor-id; and if match-expander is not #f, then a use of name in a match pattern is expanded using match-expander.

If base-info is an instance of a struct type with any of the following properties, the result has the same properties with the same values: prop:struct-auto-info, prop:struct-field-info. Other properties of base-info are not preserved.

The following example defines a point struct and then replaces point with an adjusted struct info record that adds a contract and makes one of the constructor arguments optional:
> (module point racket/base
    (provide (struct-out point))
    (struct point (x y) #:transparent))
> (module point-adjusted racket/base
    (require (for-syntax racket/base scramble/struct-info)
             racket/contract
             (rename-in 'point [point orig-point]))
    (define (make-point x [y 0]) (orig-point x y))
    (define-module-boundary-contract checked-make-point make-point
      (->* [real?] [real?] point?)
      #:name-for-blame point)
    (define-syntax point
      (adjust-struct-info (syntax-local-value #'orig-point)
                          #:constructor #'checked-make-point))
    (provide (struct-out point)))
> (require 'point-adjusted)
> (point 1 2)

(point 1 2)

> (point 3)

(point 3 0)

> (point 'hello 'goodbye)

point: contract violation

  expected: real?

  given: 'hello

  in: the 1st argument of

      (->* (real?) (real?) point?)

  contract from: 'point-adjusted

  blaming: top-level

   (assuming the contract is correct)

  at: eval:2:0

> (struct point3 point (z) #:transparent)
> (point3 1 2 3)

(point3 1 2 3)