global:   Global variables with command-line interaction
make-global
define-global
global?
global-name
global-help
global-valid?
global-string->value
global-more-commands
global-get
global-set!
global-update!
global-unsafe-set!
global-unsafe-update!
global-set-from-string!
get-globals
global->cmd-line-rule
globals->command-line
globals->assoc
globals-interact
string->boolean
7.9

global: Global variables with command-line interaction

laurent.orseau@gmail.com

 (require global) package: global

Usage: Use define-global to define global variables (possibly in different modules) with cross-module getters and setters. globals->command-line automatically generates a command line parser for all the globals defined in modules that are transitively required, and globals-interact generates and textual interaction for reading and writing globals.

Here’s a minimal expample that defines the global *burgers* and generates a command-line parser:

Globals can be used without a command-line parser.

"burger.rkt"

#lang racket/base
(require global)
 
(define-global *burgers*     ; name
  1                          ; initial value
  "Number of burgers"        ; help string for the command line
  exact-nonnegative-integer? ; validation
  string->number)            ; conversion from input string
 
(void (globals->command-line))
 
(printf "You ordered ~a burgers.\n" (*burgers*))

Save this example as burger.rkt, then on the command line (in the corresponding directory), type:
racket burger.rkt --help
then try
racket burger.rkt --burgers 10
and maybe
racket burger.rkt --burgers a

Note: A similar example is included with the package and can be run with
racket -l global/examples/minimal -- --help

For a more extensive example, including a use of globals-interact, try
racket -l global/examples/example -- --help

Additional remarks:

A global variable defined with define-global in a module A is shared between all modules that require A.

Note that a global defined in module A that is transitively required by module B can be fully accessed in module B even if A does not export any identifier.

By convention, globals’ identifiers are surrounded by *. The value of a global *my-global* can be retrieved with (*my-global*) and set with (*my-global* some-new-value).

By contrast to parameters, globals
  • always have a single value at any time,

  • are not thread safe.

Suggestions, questions or issues? File an issue.

procedure

(make-global name    
  init    
  help    
  valid?    
  string->value    
  [more-commands])  global?
  name : symbol?
  init : any/c
  help : (or/c string? (listof string?))
  valid? : (-> any/c any/c)
  string->value : (-> any/c any/c)
  more-commands : (listof string?) = '()
Returns a global variable with initial value init. The name is for printing purposes.

The procedure valid? is used when setting or updating the value of the global to check if the new value is valid. Note that valid? is not used on init: this can be useful to set the initial value to #f for example while only allowing certain values when set by the user.

The procedure string->value is used to convert command line arguments to values that are checked with valid? before setting the corresponding global to this value. (They could also be used for example in text-field% in GUI applications.)

more-commands is an optional list of additional command-line flags, which can be used in particular to specify short flags.

syntax

(define-global var args ...)

Shorthand for (define var (make-global 'var args ...)).

procedure

(global? g)  boolean?

  g : any/c
(global-name g)  symbol?
  g : global?
(global-help g)  (or/c string? (listof string?))
  g : global?
(global-valid? g)  (-> any/c boolean?)
  g : global?
(global-string->value g)  (-> string? any/c)
  g : global?
(global-more-commands g)  (listof string?)
  g : global?
Predicate and accessors. See make-global.

procedure

(global-get g)  any/c

  g : global?
(global-set! g v)  void?
  g : global?
  v : any/c
(global-update! g updater)  void?
  g : global?
  updater : (-> any/c any/c)
(global-get *g*) is equivalent to (*g*) and returns the value of the global. (global-set! *g* v) is equivalent to (*g* v). global-update! updates the value of the global based on its previous value. global-set! and global-update! raise an exception if global-valid? returns #f for the new value.

procedure

(global-unsafe-set! g v)  void?

  g : global?
  v : any?
(global-unsafe-update! g updater)  void?
  g : global?
  updater : (-> any/c any/c)
Forces setting and updating the value of the global without checking its validity with global-valid?.

procedure

(global-set-from-string! g str)  void?

  g : global?
  str : string?

procedure

(get-globals)  (listof global?)

Returns the list of globals that have not been GC’ed (even if they cannot be read directly by the module calling get-globals).

procedure

(global->cmd-line-rule g    
  #:name->string name->string    
  #:boolean-valid? bool?    
  #:boolean-no-prefix no-prefix)  list?
  g : global?
  name->string : 
(λ (n) (string-trim (symbol->string n)
                    #px"[\\s*?]+"))
  bool? : boolean?
  no-prefix : "--no-~a"
Returns a rule to be used with parse-command-line.

Booleans are treated specially on the command line, as they don’t require arguments. If the validation of g is equal? to bool? then the returned rule corresponds to a boolean flag that inverts the current value of g. For example, if bool? is boolean?, then, for

(define-global abool #t "abool" boolean? string->boolean)

the call (global->cmd-line-rule (list abool)) (only) produces a rule with the flag "--no-abool" which sets abool to #f if present on the command line, while for

(define-global abool #f "abool" boolean? string->boolean)

it (only) produces the flag "--abool" which sets abool to #t. Note that for booleans, more-commands are used as is (without being negated). Setting bool? to #f treats boolean globals as normal flags that take one argument. By default, name->string removes some leading and trailing special characters.

procedure

(globals->command-line [#:globals globals    
  #:boolean-valid? bool?    
  #:boolean-no-prefix no-prefix    
  #:mutex-groups mutex-groups    
  #:argv argv    
  #:program program]    
  trailing-arg-name ...)  any
  globals : (listof global?) = (get-globals)
  bool? : (-> any/c any/c) = boolean?
  no-prefix : string? = "--no-~a"
  mutex-groups : (listof (listof global?)) = '()
  argv : (vectorof (and/c string? immutable?))
   = (current-command-line-arguments)
  program : string? = "<prog>"
  trailing-arg-name : string?
Produces a command line parser via parse-command-line (refer to the latter for general information).

See global->cmd-line-rule for more information about boolean flags.

Each list of globals within mutex-groups are placed in a separate once-any group in parse-command-line.

Repeated flags are not supported by globals.

See also the note in (get-globals).

procedure

(globals->assoc [globals])  (listof (cons/c symbol? any/c))

  globals : (listof global?) = (get-globals)
Returns an association list of the global names and their values.

procedure

(globals-interact [globals])  void?

  globals : (listof global?) = (get-globals)
Produces a command-line interaction with the user to read and write values of globals.

procedure

(string->boolean s)  boolean?

  s : string?
Interprets s as a boolean. Equivalent to
(and (member (string-downcase (string-trim s))
             '("#f" "#false" "false"))
     #t)