scramble:   Assorted Utility Libraries
1 Conditional Expressions
cond+
and+
or+
2 Functions
K
K0
call
3 Immutable and Mutable Conversion
immutable
mutable
4 Lists
singleton?
5 Numbers
exact
inexact
min*
max*
ceiling-quotient
ceiling-multiple
floor-quotient
floor-multiple
6 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
7 Classes and Objects
init-private
constructor-style-printable<%>
get-printing-class-name
get-printing-components
print-quotable-never<%>
print-quotable-always<%>
8 Compile-time Code Injection
begin/  inject-syntax
8.1

scramble: Assorted Utility Libraries

Ryan Culpepper <ryanc@racket-lang.org>

1 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

2 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>)

3 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.

4 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

5 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.

6 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"

7 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.

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 'always.

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") ...>

8 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.