Commit 20b20f44 authored by kirk's avatar kirk

Add symbolic logic and scheme content

parent 08e42e2a
Copyright © 2014-2019 Kirk Rader. All rights reserved.
# Examples
graph TB
start(( ))
inception[Inception / Business Analysis]
execution[Execution / Development]
verification[Verification / QA]
operation[Operation / Tech Support]
start-- request feature -->inception
inception-- reject -->start
inception-- analyze -->inception
inception-- approve -->execution
execution-- design / implement -->execution
execution-- request change -->inception
execution-- release -->verification
verification-- report bug -->execution
verification-- test -->verification
verification-- request change -->inception
verification-- accept -->operation
operation-- monitor -->operation
operation-- report bug -->execution
operation-- request change -->inception
operation-- anayltics -->inception
- <>
title: "Scheme"
date: 2019-09-16T16:05:54-07:00
weight: 30
# Scheme
> As with the section on [symbolic logic][], a few gluttons for punishment
> among my colleagues asked for this.
> Ok, one day my boss asked "what's all this I hear about Scheme continuations,"
> (more or less) and the discussion eventually evolved into this.
Examples demonstrating various Scheme features and built-in procedures
- Tail recursion
- Lexical closures
- `call-with-current-continuation -- a.k.a. `call/cc`
- `dynamic-wind`
They provide simple, if somewhat contrived, samples of idioms commonly used in
Scheme_ programming.
\note These examples were created and debugged using the Guile -- -- dialect, but should work as-is in most
variants of Scheme.
- [Basics][]
- [Tail Recursion][]
- [Lexical Closures][]
- [call-with-current-continuation][]
- [dynamic-wind][]
- [engines][]
[symbolic logic]: {{< relref "/docs/symbolic-logic" >}}
[basics]: {{< relref "basics" >}}
[tail recursion]: {{< relref "tail-recursion" >}}
[lexical closures]: {{< relref "lexical-closures" >}}
[call-with-current-continuation]: {{< relref "call-with-current-continuation" >}}
[dynamic-wind]: {{< relref "dynamic-wind" >}}
[engines]: {{< relref "engines" >}}
This diff is collapsed.
This diff is collapsed.
;; -*- geiser-scheme-implementation: guile -*-
;; Copyright 2016 Kirk Rader
;; Licensed under the Apache License, Version 2.0 (the "License"); you
;; may not use this file except in compliance with the License. You
;; may obtain a copy of the License at
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; implied. See the License for the specific language governing
;; permissions and limitations under the License.
;; A Scheme program that illustrates the use of dynamic-wind to
;; protect entry, exit and re-entry of an execution context using
;; continuations.
;; This uses an object that simulates an automated drill press. The
;; simulated drill has various operational constraints, such as that
;; the main drilling motor can only be turned on or off when the bit
;; is in its highest position.
;; The point of the example is to show how dynamic-wind can be used to
;; ensure that these constraints are taken into account by an ongoing
;; sequence of drilling commands even when that sequence might be
;; exited and then re-entered using continuations.
;; Function: (drill-hole)
;; Usage: (drill-hole)
;; Outputs a sequence of messages to the console indicating a drill's
;; state changes over the course of a program to bore a hole.
;; This uses dynamic-wind to ensure that the drilling motor is safely
;; started and stopped each time the drilling program execution
;; context is entered, exited or re-entered using continuations.
;; Returns a value that indicates the outcome of the drilling program.
(define (drill-hole)
(let ((drill (make-drill)))
(letrec ((start-drilling
;; turn on the motor
;; assumes that motor is currently stopped and that bit
;; position is 0
;; this will be used as the "before thunk" in a call to
;; dynamic-wind
(lambda ()
(display "start-drilling called")
(drill 'start-motor!)))
;; raise the bit and stop the motor
;; assumes that motor is currently running
;; this will be used as the "after thunk" in a call to
;; dynamic-wind
(lambda ()
(display "stop-drilling called")
(drill 'move-bit! 0)
(drill 'stop-motor!)))
;; the drilling program
;; assumes the motor is currently running
;; stops half-way through to let the bit cool, returning a
;; continuation by which drilling can be resumed and
;; continue to completion
;; returns 'done when the hole is completely drilled
(lambda ()
(lambda (return)
(display "keep-drilling entered")
(drill 'move-bit! 50)
(display "hole 50% drilled, pausing to let bit cool")
(let ((command (call/cc (lambda (k) (return k)))))
(display "keep-drilling re-entered")
(if (eq? command 'continue)
(display "continuing to drill")
(drill 'move-bit! 100)
(display "hole completely drilled")
(display "drilling program canceled")
;; bind k to the result of a call to keep-drilling,
;; protected during stack winding and unwinding by calls to
;; start-drilling and stop-drilling, respectively
;; ensure drill motor is running every time keep-drilling
;; is entered or re-entered
;; drill the hole
;; ensure drill motor is stopped every time keep-drilling
;; exits
;; we get here twice:
;; 1. when keep-drilling returns a continuation half-way through
;; the drilling process
;; 2. when keep-drilling ultimately completes as a result of the
;; invocation of k, below
;; in *both* cases, the preceding call to dynamic-wind ensures
;; that start-drilling is called before the body of
;; keep-drilling is entered and stop-drilling is called after
;; the body of keep-drilling exits, even though those entries
;; and exits happen at different times and at different points
;; in the flow of control
(if (procedure? k)
;; k is a continuation procedure when keep-drilling exits
;; half-way throuh the drilling process
(display "waiting a bit (pun intended)")
;; use k to resume drilling
;; keep-drilling will not exit again until the hole is
;; completely bored unless an exception is thrown
(k 'continue))
;; when keep-drilling finishes boring the hole completely, it
;; returns a value that is not a procedure
;; return that final value as the result of drill-hole
title: "dynamic-wind"
weight: 80
# bookFlatSection: false
# bookShowToC: true
# dynamic-wind
The built-in `dynamic-wind` procedure is Scheme's equivalent to Common
Lisp's `unwind-protect`, the `try ... finally ...` construct in
languages like C# and Java and so on.
The main entry point for this example is a provedure named
`drill-hole`. It uses the simulated [drill press][] described
earlier. The `drill-hole` function defines several private functions
including `start-drilling`, `keep-drilling` and `stop-drilling` that
are used in the arguments passed to `dynamic-wind` in the body of
`drill-hole`. As noted in the chapter on [lexical closures][] where
[make-drill][] was defined, the simulated drill press has a number of
run-time constraints such as that the drill motor can only be started
or stopped when the drill height is 0. The `drill-hole` procedure is a
wrapper for an instance of such a simulated drill that uses
`dynamic-wind` to protect the wrapped drill from violations of such
constraints even when its `keep-drilling` loop is exited and
re-entered using a [continuation][call-with-current-continuation].
The result of calling `keep-drilling` is bound to the local variable
`k` in the body of `drill-hole`. That call to `keep-drilling` is
protected during stack winding and unwinding by calls to
`start-drilling` and `stop-drilling`, respectively, by passing all
three functions in a call to `dynamic-wind`.
Those familiar with languages with constructs like `(unwind-protect
...)` or `try ... finally ...` can understand the relationship between
`keep-drilling` and `stop-drilling` as being an enhanced version of
such constructs.
In other words, `dynamic-wind` guarantees to call `stop-drilling`
after `keep-drilling` exits, whether or not the latter exits normally
or through some non-sequential flow of control, as in the following
Common Lisp code:
Or in the case of languages like Java and C#, something like:
try {
} finally {
In C++, similar stack-unwinding protections are achieved using
"automatic" variables of types with destructors.
The additional functionality offered by `dynamic-wind` over the
`unwind-protect` and `try ... finally ...` examples is that
`dynamic-wind` also guarantees that `start-drilling` will be called
before `keep-drilling` starts execution the first time it is entered
*and* if it is re-entered by invoking a continuation after it has
previously exited.
This stack winding / unwinding / rewinding behavior is demonstrated in
the body of `drill-hole`. In particular, `keep-drilling` bores the
hole 50% through the full range of the possible bit positions and then
exits, returning a continuation that is later used to resume drilling
until the hole is 100% complete. Looking at the [output](#output),
one can see that `start-drilling` and `stop-drilling` are called the
appropriate number of times (twice each), in the appropriate places in
the overall flow of control (each time the body of `keep-drilling` is
about to be entered and after each time `keep-drilling` exits,
<a id="drill-hole"></a>
Here is the Scheme source for `drill-hole`:
{{< code file="/docs/scheme/drill-hole.scm" language="lisp" >}}
(download [drill-hole.scm][])
<a id="output"></a>
Here is the output of [(drill-hole)](#drill-hole):
start-drilling called
started motor
keep-drilling entered
stepping bit position from 0 to 50 by 1
hole 50% drilled, pausing to let bit cool
stop-drilling called
stepping bit position from 50 to 0 by -1
motor stopped
waiting a bit (pun intended)
start-drilling called
started motor
keep-drilling re-entered
continuing to drill
stepping bit position from 0 to 100 by 1
hole completely drilled
stop-drilling called
stepping bit position from 100 to 0 by -1
motor stopped
[drill press]: {{< relref "lexical-closures#make-drill" >}}
[call-with-current-continuation]: {{< relref "call-with-current-continuation" >}}
[drill-hole.scm]: /docs/scheme/drill-hole.scm
title: "Engines"
weight: 90
# bookFlatSection: false
# bookShowToC: true
# Engines
In <a
href="">Engines from
Continuations</a>, Dybvig and Hieb [1988] describe how the flow of
control abstraction known as "engines" can be implemented using
Scheme's first-class continuations. The following source code is
adapted from one of the versions of `make-engine` described by Dybvig
and Hieb.
In this implementation, an engine is a Scheme procedure of three
arguments. For example, the following code will invoke the procedure
named `thunk` as an engine:
(letrec ((return
(lambda (value remaining-ticks)
(display "engine returned ")
(display value)
(lambda (new-engine)
(display "engine expired, ")
(display "use the given new ")
(display "engine to resume")
(lambda ()
(display "engine running")
((make-engine thunk) 1 return expire))
This is different from simply calling `thunk` directly in that it will
be given a finite amount of "fuel" in the form of "timer ticks." If
`thunk` completes before the engine runs out of fuel, it will call
*return*. If the fuel runs out before `thunk` completes, *expire* will
be invoked with a new engine that can be used to resume the
interrupted computation in `thunk`.
> **Warning:** See the warnings in the source code comments regarding
> the use of `decrement-timer` in procedures passed to `make-engine`.
This implementation differs from that in the original paper in a few
ways, all thoroughly documented in comments in the source code. Of
particular interest:
- Eliminates the need for the `make-simple-engine` helper function
defined and used in the paper
(Specifically, `make-engine` wraps the invocation of the procedure
it was passed in a call to `engine-return`)
- Adds `engine-expire` for symmetry with `engine-return`
(Calling `engine-expire` causes the currently executing engine to
pass control to its expiration handler immediately in the same way
that calling `engine-return` causes an immediate invocation of the
return handler)
<a id="engines"></a>
{{< code file="/docs/scheme/engines.scm" language="lisp" >}}
(download [engines.scm][])
[engines.scm]: /docs/scheme/engines.scm
This diff is collapsed.
title: "Lexical Closures"
weight: 60
# bookFlatSection: false
# bookShowToC: true
# Lexical Closures
The observant reader may have noticed that examples of mutually
recursive `lambda` expressions in a number of places were shown using
disjoint sets of variable names, as in the basic example of \ref
scheme-composition "function composition". This was done mainly for
clarity of exposition: it is very obvious which variables "belong" to
which functions when they all have different names where their lexical
or dynamic scopes overlap.
This is not actually a requirement of Scheme, where normal `lambda`
bindings are lexically scoped. For example, the same example of
function composition could have be written with both functions having
overlapping sets of variable names with no change in meaning or
((lambda (f x) (f x))
(lambda (x) (+ x 1))
It simply would have been a bit harder to describe what was going
since in this case there are two *different* variables both named `x`.
> Historically, not all Lisp dialects have been lexically scoped in
> this way. Bindings for all variables existed in a single global
> environment in very early implementations of Lisp. This fact meant
> that there were times that the programmer actually did have to take
> care to use disjoint variable names for mutually recursive functions
> to avoid "name collisions" that could affect the outcome of a
> computation. That "bug" was sometimes exploited as a "feature" by
> some algorithms, so modern lexically scoped Lisp dialects often have
> alternative versions of special forms like `lambda`, `let` etc. that
> share a single, global name space. Some Scheme dialects, for
> example, have defined things like a `fluid-let` special form that
> works like ordinary `let` but whose bindings are in a single "fluid"
> (a.k.a. "dynamic") environment. Guile, which is used for the
> examples in this document, adopts the mechanisms defined by <a
> ref="">SRFI-39</a>.
But wait! There's more!
If all there were to a `lambda` function's variable bindings that they are
lexically scoped, they would not be substantially different from, say, pointers
to functions in languages like C or anonymous functions in languages like Java.
The lexical environments that are created by `lambda` binding in Scheme (and
other modern dialects like `Common Lisp`) represent "closures" over their entire
run-time context such that a function object, when treated as a data value,
"remembers" the lexical environment in which it was created. Mutliple
invocations of the same such function create distinct such "closed"
environments, also known as "closures." Consider:
(define (make-foo a)
(lambda () a))
(define foo1 (make-foo 1))
(define foo2 (make-foo 2))
After evaluating the three preceding expressions in a Scheme
environment, you will have three global variables, all of which are
bound to functions.
The function `make-foo` returns an anonymous function whose lexical
closure includes a definition for the local variable `a`. Each
anonymous function returned by `make-foo` "remembers" the value which
was passed to `make-foo` when it was invoked with its own "closed
over" binding of `a`. Both `foo1` and `foo2` are bound to functions
made using calls to `make-foo`, but with different values for `a`. In
particular, invoking `foo1` returns 1 while invoking `foo2` returns 2:
What this means is that anonymous functions in Scheme can be used in
many cases where you might need to define some custom data structure
in other programming languages. In fact, lexical closures are so
powerful that there is a saying among Scheme programmers to the effect
that "objects are a poor man's closures."
Here is an extended example, demonstrating the point:
<a id="make-drill"></a>
{{% code file="/docs/scheme/make-drill.scm" language="lisp" %}}
(download [make-drill.scm][])
The variable names and embedded comments make the preceding example
fairly self-explanatory. Suffice it to say here that `make-drill` is a
function that returns "instances" of "objects" that are really just
anonymous functions created by evaluating a `lambda` expression, where
the object's attributes are stored in a closed lexical
environment. The drill "instances" provide functionality that is
accessed by invoking them as functions (since that is all they really
are), as in:
(let ((drill (make-drill)))
(drill 'start-motor!)
(drill 'move-bit! 50)
(drill 'move-bit! 0)
(drill 'stop-motor!))
[make-drill.scm]: /docs/scheme/make-drill.scm
;; -*- geiser-scheme-implementation: guile -*-
;; Copyright 2016 Kirk Rader
;; Licensed under the Apache License, Version 2.0 (the "License"); you
;; may not use this file except in compliance with the License. You
;; may obtain a copy of the License at
;; Unless required by applicable law or agreed to in writing, software
;; distributed under the License is distributed on an "AS IS" BASIS,
;; implied. See the License for the specific language governing
;; permissions and limitations under the License.
;; Function: (make-drill)
;; Create a simulated drill press.
;; Usage: (let ((drill (make-drill)))
;; (drill 'start-motor!)
;; (drill 'move-bit! 50)
;; (drill 'move-bit! 0)
;; (drill 'stop-motor!))
;; A drill has two attributes:
;; motor-state: 'stopped or 'running (initially 'stopped)
;; bit-position: integer between 0 and 100 (initially 0)
;; A bit position of 0 indicates that the bit is in its highest
;; position while 100 indicates that the bit is in its lowest
;; position.
;; It supports the following messages:
;; 'get-motor-state - returns current motor-state
;; 'get-bit-position - returns current bit-position
;; 'move-bit! new-position - simulates a stepper motor controlling
;; bit height
;; 'start-motor! - set motor-state to 'running
;; 'stop-motor! - set motor-state to 'stopped
;; the message handlers collectively perform checks to enforce the
;; following constraints:
;; * the bit position must be an integer between 0 and 100 (inclusive)
;; * the motor can only change state when the bit position is 0
;; * the bit can be greater than 0 only when the motor is running
(define (make-drill)
(let ((motor-state
;; state of the main motor
;; value determined by a stepper motor
;; controlling the height of the bit
;; A drill is a function that takes a message and additional
;; arguments that depend on the particular message
(lambda (message . arguments)
(letrec ((signum
;; return 0, 1 or -1 based on the sign of the given
;; number
(lambda (x)
(cond ((negative? x) -1)
((positive? x) 1)
(else 0))))
;; instruct the stepper motor to move the bit to the
;; specified height
(lambda (new-position increment)
(display "stepping bit position from ")
(display bit-position)
(display " to ")
(display new-position)
(display " by ")
(display increment)
(let loop ((done (= bit-position new-position)))
(unless done
(set! bit-position (+ bit-position increment))
(loop (= bit-position new-position))))))
;; set the value of bit-position or throw an exception
;; if constraints would be violated
;; see set-bit-position!
(lambda (new-position)
(cond ((not