10. Subroutines

Introduction

; Note: Some of the examples will show code blocks in this style:
;
;  (define
;    ... code here ...
;  )
;
; This is not generally considered good style, and is not recommended;
; it is only used here to more clearly highlight block scope 

; By convention a 'global variable' i.e. a variable that is defined at
; the top-level, and as such, visible within any scope, is named with
; beginning and ending asterisks [and one to be used as a constant
; with beginning and ending plus signs]

(define *greeted* 0)

(define (hello)
  (set! *greeted* (+ *greeted* 1))
  (print "hi there!, this procedure has been called" *greeted* "times"))

(define (how-many-greetings) *greeted*)

;; ------------

(hello)

(define *greetings* (how-many-greetings))

(print "bye there!, there have been" *greetings* "greetings so far")

Accessing Subroutine Arguments

; Subroutine parameters are named [whether directly, or indirectly in
; the case of variable arguments - see next example]; this is the only
; means of access [This contrasts with languages like Perl and REXX which
; allow access to arguments via array subscripting, and function calls,
; respectively]
(define (hypotenuse side1 side2)
  (sqrt (sum (* side1 side1) (* side2 side2))))

(define *diag* (hypotenuse 3 4))

;; ----

; 'other-sides' is the name of a list of containing any additional
; parameters. Note that a name is still used to access values
(define (hypotenuse side1 . other-sides)
  (let ((all-sides (cons side1 other-sides)))
    (for-each
      (lambda (side) 
        ;; ...
        '())
      all-sides)
  ;; ...
  '()))

;; ----

(define *diag* (hypotenuse 3 4))

;; ----

; Possible to pack parameters into a single structure [e.g. list or
; array], and access values contained therein
(define (hypotenuse sides)
  (let ((side1 (car sides)) (side2 (caar sides)))
    (sqrt (sum (* side1 side1) (* side2 side2)))))

;; ----

(define *args* '(3 4))
(define *diag* (hypotenuse *args*))

;; ------------

; Parameters passed by reference, however, whether original object is
; modified depends on choice of functions used to manipulate them
; [most functions create copies and return these; mutating versions of
; same functions may also exist [see next example] 
(define *nums* (vector 1.4 3.5 6.7))

(define (int-all vec)
  (vector-map-in-order
    (lambda (element) (inexact->exact (round element)))
    vec))

; Copy created
(define *ints* (int-all *nums*))

(print *nums*)
(print *ints*)

;; ----

(define *nums* (vector 1.4 3.5 6.7))

(define (trunc-all vec)
  (array-map-in-order!
    (lambda (element) (inexact->exact (round element)))
    vec))

; Original modified
(trunc-all *nums*)

Making Variables Private to a Function

; Scheme is lexically-scoped; variables defined within a block are
; visible only within that block. Whilst nested / subordinate blocks
; have access to those variables, neither the caller, nor any called
; procedures have direct access to those same variables

(define (some-func parm1 parm2 parm3)
  ;; ... paramaters visible here ...

  (let ((var1 1) (var2 2) (var3 3))
    ;; ... parameters also visible here, but variables, 'var1' etc
    ;;     only visible within this block ...
  )
  ;; ... paramaters also visible here, but still within procedure body ...
)

;; ------------

; Top-level definitions - accessable globally 
(define *name* (caar (command-line)))
(define *age* (cadr (command-line)))

(define *start* (fetch-time))

;; ----

; Lexical binding - accessable only within this block
(let ((name (caar (command-line)))
      (age (cadr (command-line)))
      (start (fetch-time)))
   ;; ... variables only visible here ...
   '()
)

;; ------------

(define *pair* '(1 . 2))

; 'a' and 'b' need to be dereferenced and separately defined [Also,
; since globally defined, should really be named, '*a*', '*b*', etc]
(define a (car *pair*))
(define b (cdr *pair*))
(define c (fetch-time))

(define (run-check)
  ;; ... do something with 'a', 'b', and 'c' ...
  '()
)

(define (check-x x y)
  (if (run-check)
    (print "got" x)))

; Calling 'check-x'; 'run-check' has access to 'a', 'b', and 'c'
(check-x 1 2)

;; ----

; If defined within a block, variables 'a', 'b', and 'c' are no longer
; accessable anywhere except that scope. Therefore, 'run-check' as
; defined above can no longer access these variables [in fact, the code
; will fail because variables 'a', 'b', and 'c' do not exist when
; 'run-check' is defined]
(let ((a (car *pair*))
      (b (cdr *pair*))
      (c (fetch-time)))
   ;; ...
   (check-x 1 2)  
   ;; ...
   '()
)

;; ----

; The procedures, 'run-check' and 'check-x' are defined within the
; same block as variables, 'a', 'b', and 'c', so have direct access to
; them
(let* ((a (car *pair*))
       (b (cdr *pair*))
       (c (fetch-time))

       (run-check
         (lambda () 
           ;; ... do something with 'a', 'b', and 'c' ...
           '()))

       (check-x
         (lambda (x y)
           (if (run-check)
             (print "got" x)))) )
   ;; ...
   (check-x 1 2)  
   ;; ...
   '()
)

Creating Persistent Private Variables

; Ordinarily, a variable must be initialised when it is defined,
; whether at the top-level: 
(define *variable* 1)

; ... or within a 'let' binding
(let* ((variable 1)
       (mysub
         (lambda () 
           ;; ... accessing 'variable' ...
         '())))
  ;; ... do stuff ...
  '()
)

; However, since Scheme allows syntactic extensions via 'macros' [of
; which there are two varieties: hygenic and LISP-based], it is
; possible to create new forms which alter this behaviour. For example,
; in this tutorial: http://home.comcast.net/~prunesquallor/macro.txt
; there is a macro implementation equivalent to 'let, 'called,
; 'bind-values', which allows variables to be defined without initial
; values; an example follows:

; Initialisation values for 'a' and 'b' not specified
(bind-values ((a) b (c (+ *global* 5)))
  ;; ... do stuff ...
  '()
)

; In Scheme many things are possible, but not all those things are
; offered as standard features :) !

;; ------------

(let* ((counter 42)
       (next-counter
         (lambda () (set! counter (+ counter 1)) counter))
       (prev-counter
         (lambda () (set! counter (- counter 1)) counter)))

  ;; ... do stuff with 'next-counter' and 'prev-counter' ...
  '()
)

;; ----

; A more complete, and practical, variation of the above code:

; 'counter' constructor
(define (make-counter start)
  (let* ((counter 42)
         (next-counter
           (lambda () (set! counter (+ counter 1)) counter))
         (prev-counter
           (lambda () (set! counter (- counter 1)) counter)))
  (lambda (op)
    (cond
      ((eq? op 'prev) prev-counter)
      ((eq? op 'next) next-counter)
      (else (lambda () (display "error:counter"))) ))))

; Interface functions to 'counter' functionality
(define (prev-counter counter) (apply (counter 'prev) '()))
(define (next-counter counter) (apply (counter 'next) '()))

; Create a 'counter'
(define *counter* (make-counter 42))

; Use the 'counter' ...
(print (prev-counter *counter*))
(print (prev-counter *counter*))
(print (next-counter *counter*))

Determining Current Function Name

; Scheme interpreters generally provide a rich collection of procedure
; metadata, as well as easy access to a program's current 'execution
; state'. Put simply, provision of a powerful, highly customisable
; debugging / tracing facility is almost taken for granted. However, using
; it to perform as trivial a task as obtaining the current function name
; is less than trivial [at least it seems so in Guile] as it appears to
; require quite some setup work. Additionally, the documentation talks
; about facilities e.g. trap installation, that don't appear to be
; available [at least, I couldn't find them].
;
; Example below uses in-built debugging facilities to dump a backtrace
; to a string port and extract the caller's name from the resulting
; string. Not exactly elegant ...

; Execute using: guile --debug ... else no useful output seen
(use-modules (ice-9 debug))

(define (child num)
  ; Create stack [i.e. activation record] object, discarding
  ; irrelevant frames
  (let ((s (make-stack #t 3 1))
        (trace-string-port (open-output-string))
        (parent-name ""))

    ; Dump backtrace to string port
    (display-backtrace s trace-string-port)

    ; Extract caller's name from backtrace data
    ; [shamefully crude - don't do this at home !]
    (set! parent-name
      (caddr (string-tokenize
               (cadr (string-split
                       (get-output-string trace-string-port)
                       #\newline))
               char-set:graphic)))

    ; Who's your daddy ?
    (print parent-name)))

; Each invocation of 'child' should see 'parent' displayed as
; the caller
(define (parent)
  (child 1)
  (child 2)
  (child 3))

(parent) 

Passing Arrays and Hashes by Reference

; Procedure parameters are references to entities, so there is no special
; treatment required. If an argument represents a mutable object such
; as an array, then care should be taken to not mutate the object within
; the procedure, or a copy of the object be made and used

(array-diff *array1* *array2*)

;; ------------

(define (add-vector-pair x y)
  (let* ((vector-length (vector-length x))
         (new-vec (make-vector vector-length)))
    (let loop ((i 0))
      (cond 
        ((= i vector-length) new-vec)
        (else
          (vector-set! new-vec i (+ (vector-ref x i) (vector-ref y i)))
          (loop (+ i 1)) ))) ))

;; ----

(define *a* '#(1 2))
(define *b* '#(5 8))

(define *c* (add-vector-pair *a* *b*))

(print *c*)

;; ----

  ;; ...

  (if (and (vector? a1) (vector? a2))
    (print (add-vector-pair a1 a2))
  ;else
    (print "usage: add-vector-pair a1 a2"))

  ;; ...

Detecting Return Context

; AFAIK there is no Scheme equivalent to Perl's 'return context' where
; it is possible to use language primitives [e.g. 'wantarray'] to 
; dynamically specify the return type of a procedure. It is, however,
; possible to:
; * Return one of several types from a procedure, whether based on 
;   processing results [e.g. 'false' on error, numeric on success], or
;   perhaps specified via control argument
; * Check procedure return type and take appropriate action

(define (my-sub)
  (let* ((datatype (vector '() 7 '(1 2 3) "abc" 'sym)))
    (vector-ref datatype (random (vector-length datatype))) ))

;; ----

; '*result*' is bound to a randomly chosen datatype
(define *result* (my-sub))

(cond
  ; It is common to return an empty list to represent 'void'
  ((null? *result*) (print "void context"))

  ((list? *result*) (print "list context"))
  ((number? *result*) (print "scalar context"))
  ((string? *result*) (print "string context"))
  ((symbol? *result*) (print "atom context"))
  (else (print "Unknown type")))

Passing by Named Parameter

; Keyword parameters are fully supported. Note that pairs have
; replaced Perl strings in the examples since they are easier to
; manipulate

(use-modules (ice-9 optargs))

(define* (the-func #:key (increment (cons 10 's))
                         (finish (cons 0 'm))
                         (start (cons 0 'm)))
  (print increment)
  (print finish)
  (print start))

(the-func)
(the-func
  #:increment (cons 20 's) #:start (cons 5 'm) #:finish (cons 30 'm))
(the-func #:start (cons 5 'm) #:finish (cons 30 'm))
(the-func #:finish (cons 30 'm))
(the-func #:start (cons 5 'm) #:increment (cons 20 's))

Skipping Selected Return Values

;; @@INCOMPLETE@@
;; @@INCOMPLETE@@

Returning More Than One Array or Hash

; The return of multiple values, whether arrays or other items, may be 
; achieved via:
; * Packaging return items as a single list, structure or array, an
;   approach which is usable across many languages, though can be
;   clunky because the procedure caller must manually extract all
;   items
; * The 'values' procedure, a more Schemish idiom, is usually used in
;   conjunction with the 'call-with-values' procedure [the former combines
;   multiple values, the latter captures and cleanly extracts them]. It
;   comes into its own, however, when used to create a 'macro' [an
;   extension to the Scheme language] like 'let-values', a variation of
;   the 'let' form that allows multiple return values to be placed directly
;   into separate variables. Implementation shown here is from 'The
;   Scheme Programming Language, 3rd Edition' by R. Kent Dybvig, though
;   there exists a more standard implementation in SRFI-11. There is also
;   the 'receive' functionality accessable via: (use-modules (ice-9 receive))

; [1] Implementation of 'somefunc' returning muliple values via packaging
; items within a list that is returned
(define (somefunc)
  (let ((a (make-vector 5))
        (h (make-hash-table 5)))
    (list a h) ))

; Retrieving procedure values requires that the return list be captured
; and each contained item separately extracted ['let*' used in place of
; 'let' to ensure correct retrieval order]
(let* ((return-list (somefunc))
       (a (car return-list))
       (b (cadr return-list)))

  ;; ... do something with 'a' and 'b' ...
  '())

;; ----------------------------

; [2] Implementation of 'somefunc' returning muliple values using the
; 'values' procedure 

(use-syntax (ice-9 syncase)) 

; 'let-values' from: http://www.scheme.com/tspl3/syntax.html#fullletvalues
(define-syntax let-values
  (syntax-rules ()
    ((_ () f1 f2 ...) (let () f1 f2 ...))
    ((_ ((fmls1 expr1) (fmls2 expr2) ...) f1 f2 ...)
     (lvhelp fmls1 () () expr1 ((fmls2 expr2) ...) (f1 f2 ...))))) 

(define-syntax lvhelp
  (syntax-rules ()
    ((_ (x1 . fmls) (x ...) (t ...) e m b)
     (lvhelp fmls (x ... x1) (t ... tmp) e m b))
    ((_ () (x ...) (t ...) e m b)
     (call-with-values
       (lambda () e)
       (lambda (t ...)
         (let-values m (let ((x t) ...) . b)))))
    ((_ xr (x ...) (t ...) e m b)
     (call-with-values
       (lambda () e)
       (lambda (t ... . tmpr)
         (let-values m (let ((x t) ... (xr tmpr)) . b))))))) 

;; ------------

(define (somefunc)
  (let ((a (make-vector 5))
        (h (make-hash-table 5)))
    (values a h) ))

; Multiple return items placed directly into separate variables
(let-values ( ((a h) (somefunc)) )
  (print (array? a))
  (print (hash-table? h)))

Returning Failure

; Like most modern languages, Scheme supports exceptions for handling
; failure, something that will be illustrated in another section. However,
; conventions exist as to the choice of value used to indicate failure:
; * Empty list i.e. '() is often used for this task, as is it's string
;   counterpart, "", the empty string
; * Return false i.e. #f to indicate failed / not found etc, and a valid
;   value otherwise [e.g. testing set membership: if not a member, return
;   #f, but if a member, return the item itself rather than #t]

; Return empty list as indicating 'failure'
(define (sub-failed) '())

;; ------------

(define (look-for-something)
  ...
  (if (something-found)
    ; Item found, return the item
    something
  ;else
    ; Not found, indicate failure
    #f
  ))

;; ----

(if (not (look-for-something))
  (print "Item could not be found ...")
;else
  ;; do something with item ...
  ;; ...
  '()

;; ------------

; An interesting variation on returning #f as a failure indicator is
; in using the, 'false-if-exception' procedure whereby a procedure is
; executed, any exceptions it may throw caught, and handled by simply
; returning #f. See example in section on Exception Handling below.

;; ------------

(define (ioctl) 
  ;; ...
  #f)

(or (ioctl) (begin (print "can't ioctl") (exit 1)))

Prototyping Functions

; Whether Scheme is seen to support prototyping depends on the definition
; of this term used:
; * Prototyping along the lines used in Ada, Modula X, and even C / C++,
;   in which a procedure's interface is declared separately from its
;   implementation, is *not* supported
; * Prototyping in which, as part of the procedure definition, parameter 
;   information must be supplied. This is a requirement in Scheme in that
;   parameter number and names must be given, though there is no need to
;   supply type information [optional and keyword parameters muddy the
;   waters somewhat, but the general principle applies]

(define (func-with-no-arg) ...)
(define (func-with-one-arg arg1) ...)
(define (func-with-two-arg arg1 arg2) ...)
(define (func-with-three-arg arg1 arg2 arg3) ...)

Handling Exceptions

; Not exactly like the Perl example, but a way of immediately
; exiting from an application
(define (die msg . error-code)
  (display (string-append msg "\n") (current-error-port))
  (exit (if (null? error-code) 1 (car error-code))))

;; ----

(die "some message")

;; ------------

; An exception is thrown via 'throw'; argument must be a symbol
(throw 'some-exception)

; Invalid attempts - these, themselves force a 'wrong-type-arg
; exception to be thrown
(throw #t)
(throw "my message")
(throw 1)

;; ------------

; Example of a 'catch all' handler - 'proc' is executed, and any
; exception thrown is handled, in this case by simply returning false
(define (false-if-exception proc)
  (catch #t
    proc
    (lambda (key . args) #f)))

(define (func)
  (print "Starting 'func' ...")
  (throw 'myexception 1)
  (print "Leaving 'func' ..."))

;; ----

(if (not (false-if-exception main))
  (print "'func' raised an exception")
  (print "'func' executed normally"))

;; ------------

; More typical exception handling example in which:
; * 'func' is executed
; * 'catch' either:
;   - returns return value of 'func' [if successful]
;   - executes handler(s)

(define (full-moon-exception-handler key . args)
  (print "I'm executing after stack unwound !"))

(define (full-moon-exception-prewind-handler key . args)
  (print "I'm executing with the stack still intact !"))

(define (func)
  (print "Starting 'func' ...")
  (throw 'full-moon-exception 1)
  (print "Leaving 'func' ..."))

(catch 'full-moon-exception
   func
   full-moon-exception-handler
   full-moon-exception-prewind-handler)

Saving Global Values

; Scheme is lexically-scoped, so same-name, higher-level variables
; are merely shadowed in lower-level blocks. Upon exit from those
; blocks the higher-level values are again available. Therefore, the
; saving of global variables, as required by Perl, is not necessary

; Global variable
(define age 18)

; Procedure definition creates a closure - it captures the earlier
; version of, age', and will retain it
(define (func)
  (print age))

(if (condition)
  ; New 'local' variable created which acts to shadow the global
  ; version
  (let ((age 23))

    ; Prints 23 because the global variable is shadowed within 
    ; this block 
    (print age)

    ; However, lexical-scoping ensures 'func' still accesses the
    ; 'age' which was active when it was defined
    (func) ))

; The use of 'fluid-let' allows for similar behaviour to Perl's i.e.
; it mimics dynamic scope, but it does so cleanly in that once its
; scope ends any affected global variables are restored to previous
; values
(if (condition)

  ; This does not create a new 'local' variables but temporarily
  ; sets the global variable, 'age' to 23
  (fluid-let ((age 23))

    ; Prints 23 because it is accessing the global version of 'age'
    (print age)

    ; Prints 23 because it is its lexically-scoped version of 'age'
    ; that has its value altered, albeit temporarily
    (func) ))

Redefining a Function

; Define two procedures, bind them to identifiers
(define (grow) (print "grow"))
(define (shrink) (print "shrink"))

; Separate procedures executed
(grow)
(shrink)

; Rebind identifier; now acts as alias for latter
(define grow shrink)

; Same procedure executed in both cases
(grow)
(shrink)

;; ------------

; As for previous except that rebinding is localised and
; ends once local scope exited
(let ((grow shrink))
  (grow)
  (shrink))

;; ----------------------------

; Example of dynamically creating [from text data] and binding
; procedures. The example here is conceptually similar to the Perl
; example in that it makes use of an 'eval' type of facility to
; generate code from text. In Scheme such tasks are generally better
; dealt with using macros 

; List of procedure name / first argument pairs
(define *colours*
  (list
    '("red" . "baron")
    '("blue" . "zephyr")
    '("green" . "beret")
    '("yellow" . "ribbon")
    '("orange" . "county")
    '("purple" . "haze")
    '("violet" . "temper") ))

; Build a series of procedures dynamically by traversing the
; *colours* list and obtaining:
; * Procedure name from first item of pair
; * Procedure argument from second item of pair
(for-each
  (lambda (colour)
    (let ((proc-string
            (string-append
              "(define " (car colour) " (lambda () "
              "\"<FONT COLOR=" (car colour) ">" (cdr colour)
              "</FONT>\"))" )))
      (eval-string proc-string)))
   *colours*)

; Apply each of the dynamically-built procedures
(for-each
  (lambda (colour)
    (print (apply (string->procedure (car colour)) '())))
  *colours*)

Trapping Undefined Function Calls with AUTOLOAD

; AFAICT Guile doesn't implement an AUTOLOAD facility in which a
; 'replacement' function is available should another one fail to
; load [though there is an autoload feature available with modules
; which is a load-on-demand facility aimed at conserving memory and
; speeding up initial program load time].
;
; One might think it would be feasable, however, to use exception
; handling to provide roughly similar functionality:

; Catch all exceptions
(catch #t
  ; Undefined procedure, 'x'
  x
  ; Exception handler could load missing code ?
  (lambda (key . args) ... ))

; However, an undefined function call is reported as:
;
;    ERROR: Unbound variable: ...
;
; and this situation doesn't appear to be user-trappable. 
;

Nesting Subroutines

; Both implementations below  are correct, and exhibit identical
; behaviour 

(define (outer arg)
  (let* ((x (+ arg 35))
         (inner (lambda () (* x 19))))
    (+ x (inner))))

;; ----------------------------

(define (outer arg)
  (let ((x (+ arg 35)))
    (define (inner) (* x 19))
    (+ x (inner))))

Program: Sorting Your Mail

;; @@INCOMPLETE@@
;; @@INCOMPLETE@@