Genera Notes, Part 1/N

I think I’d like to start sharing some screenshots and notes taken while playing with my local Open Genera installation.  In part for historical capture reasons, and in part because I think it’s fun.

I set up the system using the instructions in this Youtube video, which can be tl;dr’d as:

  • install an old-ass Ubuntu in a Virtualbox
  • configure NFS and other random things
  • SSH in with Putty and fire up an Open Genera instance via X Windows

The old-ass Ubuntu is allegedly necessary due to some behavior in newer X that breaks Open Genera, but I haven’t verified that yet, only read it.

I’m planning to write up the (Virtualbox on Windows) installation process shown in that video soon for my own future reference.  At that point I’ll probably write a script to automate it as well.

If you don’t use Windows, there is already this excellent tutorial: Running Open Genera 2.0 on Linux.  I’ve exchanged mail with the author of this piece, he seems like quite a nice guy in addition to being pretty knowledgeable.  Apparently Open Genera runs more robustly on a Compaq AlphaServerDS10L (or similar machine) as was originally intended, though it’s much slower than modern systems.

13 November 2018

Currently reading the Genera User’s Guide section entitled Getting Acquainted with Dynamic Windows (link is to the exact page of the PDF on Internet Archive!).  There is a list of bookmarks on the right that I’d like to revisit and finish reading.

To add a document to the Bookmarks pane in Document Examiner, either:

  • Visit it (so it’s added automatically)
  • From somewhere else in the interface, when you see a link (AKA a hoverable title for a document or section thereof), press Shift and then click with the left mouse button (also denoted as Sh-Mouse-M by the system – you need a three-button mouse to use this system properly)

Note: the excessive (?) whitespace in the screenshot below is due to the fact that we’re running at 1920×1080, which is my laptop’s default resolution but is probably (?) larger than any physical Lisp Machine monitor that ever existed.  Based on some pictures of actual monitors I’ve seen, I wonder if this environment would profit from running on a vertically oriented monitor as well.  Something to play with.

As I read various docs, I’ve been taking notes in Zmacs.  The Zmacs buffer shown in the next screenshot is actually getting written to the Linux machine’s (virtual) disk, and can thus be backed up, edited from other text editors, etc.  It’s all happening over NFS!  And as you have probably deduced from the window borders, this Genera window is being served over the X Windows system (specifically XMing running on Windows).

Here’s the Zmacs window after being expanded using the System menu (shown, which can be accessed at any time via Sh-Mouse-R):

In addition to the Genera window, there is the “cold load” window that is also displayed via X Windows while Open Genera is running.  And lo!  As I began writing this, Genera crashed by trying to display an ellipse (an image from the ZMail documentation, specifically Conceptual Zmail Architecture), which caused it to try to reference an array out of bounds (I don’t know why, yet).  Here’s the backtrace as shown in the cold load window (the Genera window with Document Examiner just beeped and froze – when that happens it’s time to look at the cold load window):

In the cold load window’s debugger I was able to ascertain the following keys’ meanings, at least on my laptop (confusingly, the keys here do not map to the same keys as in the Genera X window):

  • Shift-E means “eval (program)”, dropping you into Lisp
  • * (asterisk) means Abort.  It popped me back up out of the cold load stream and into Genera (the Document Examiner in particular).  My document window state was nuked, but I was able to click the bookmark to return to the section I was reading.  (Now to go back and see if I can get Document Examiner to crash again with a bad array subscript by viewing that page again!).

Note: the above crash(es) happened while I was simultaneously loading CLIM in the Listener, which seems to put a lot of load on the (smallish, Virtualbox’d) system I’m running on.  So it may have something to do with that.  Here’s what some of the output of loading CLIM looks like:

Oh, and another thing: Zmail can read GNU Emacs RMAIL files according to the docs!  I’m not 100% sure what “UNIX” mail format means in this context, but perhaps it means good old mbox?

Anyway I’ve got a lot left to explore on this system.  As it is I’ve been reading the documentation and browsing around in my off hours for the last week or two, and it feels like I’m just getting started.

Advent of Code, Day 3

In this post I’ll describe my solution for Day 3 of the Advent of Code.

Problem Description

Day 3: Perfectly Spherical Houses in a Vacuum

Santa is delivering presents to an infinite two-dimensional grid of houses.

He begins by delivering a present to the house at his starting location, and then an elf at the North Pole calls him via radio and tells him where to move next. Moves are always exactly one house to the north (‘^’), south (‘v’), east (‘>’), or west (‘<‘). After each move, he delivers another present to the house at his new location.

However, the elf back at the north pole has had a little too much eggnog, and so his directions are a little off, and Santa ends up visiting some houses more than once. How many houses receive at least one present?

For example:

‘>’ delivers presents to 2 houses: one at the starting location, and one to the east.

‘^>v<‘ delivers presents to 4 houses in a square, including twice to the house at his starting/ending location.

‘^v^v^v^v^v’ delivers a bunch of presents to some very lucky children at only 2 houses.


Broadly speaking, my solution consisted of:

  • Reading the directions file to determine the largest x and y values of the grid
  • Making a shaped array using those dimensions (specifically, we double the array dimensions to allow for movement up, down, forward, and back)
  • Starting in the center of the shaped array, follow the instructions from the “map” and mark every house (called a “position” in the code) if it hasn’t already been visited
  • Every time we visit a house we haven’t already visited, we bump a counter

Here’s the Scheme code that accomplishes those steps:

;; read in the string
;; sum the ^ and v chars to get the height of the matrix (graph)
;; sum the < and > chars to get the width of the matrix (graph)

(define (north? ch) (char=? ch #\^))
(define (south? ch) (char=? ch #\v))
(define (east? ch) (char=? ch #\>))
(define (west? ch) (char=? ch #\<))

(define (make-shape width height)
  ;; Int Int Int -> Shape
  (shape 0 width 0 height))

(define (read-shape-file file)
  ;; Pathname -> Shape
  (with-input-from-file file
    (lambda ()
      (let loop ((width 0)
         (height 0)
         (min-width  0)
         (max-width 0)
         (min-height 0)
         (max-height 0)
         (ch (read-char)))
    (if (eof-object? ch)
         (* 2 (- max-width min-width))
         (* 2 (-  max-height min-height)))
        (cond ((north? ch)
           (loop width (+ height 1)
             min-width max-width
             (min min-height height)
             (max max-height height)
          ((south? ch)
           (loop width (- height 1)
             min-width max-width
             (min min-height height)
             (max max-height height)
          ((east? ch)
           (loop (+ width 1) height
             (min min-width width)
             (max max-width width)
             min-height max-height
          ((west? ch)
           (loop (- width 1) height
             (min min-width width)
             (max max-width width)
             min-height max-height
          (else (error "WHOA"))))))))

;; We make a shaped, multi-dimensional array (SRFI-25) in the size
;; it's determined we need by our earlier check.

(define (make-grid shape)
  ;; Shape -> Array
  (make-array shape #f))

;; The POSITION data type

(define-record-type position
  (make-position x y)
  (x position-x set-position-x!)
  (y position-y set-position-y!))

(define (array-center arr)
  ;; Array -> Position
  (let ((len-x (array-length arr 0))
    (len-y (array-length arr 1)))
    (make-position (/ len-x 2)
           (/ len-y 2))))

(define (make-relative-position x y ch)
  ;; Int Int Char -> Position
  (let ((vals '()))
    (cond ((north? ch)
       (set! vals (list (+ x 1) y)))
      ((south? ch)
       (set! vals (list (- x 1) y)))
      ((east? ch)
       (set! vals (list x (- y 1))))
      ((west? ch)
       (set! vals (list x (+ y 1))))
      (else (set! vals (list x y))))
    (make-position (first vals)
           (second vals))))

(define (visited? arr x y)
  ;; Array Int Int -> Bool
  (array-ref arr x y))

(define (set-visited! arr x y)
  ;; Array Int Int -> Undefined
  (array-set! arr x y #t))

(define (visit-locations arr file)
  ;; Pathname -> Int
  (let ((visited-count 0))
    (with-input-from-file file
      (lambda ()
    (let loop ((ch (read-char))
           (current-position (array-center arr)))
      (if (eof-object? ch)
        (let* ((current-x (position-x current-position))
               (current-y (position-y current-position))
            (make-relative-position current-x current-y ch)))
          (if (not (visited? arr current-x current-y))
            (set! visited-count (+ visited-count 1))
            (set-visited! arr current-x current-y)
            (loop (read-char)
              (loop (read-char) next-position))))))))

;; eof

Once this code is loaded up in the REPL, you can use it as shown below. (Note that the answer shown at the end isn’t real to avoid a spoiler.)

(set! *the-array* (make-grid (read-shape-file (expand-file-name "~/Code/personal/advent-of-code/03.dat"))))

> (array-size *the-array*)

> (array-center *the-array*)

> (define *the-file* (expand-file-name "~/Code/personal/advent-of-code/03.dat"))

> (visit-locations *the-array* *the-file*)

Related Posts

Advent of Code, Day 2

This post describes my solution for Day 2 of the Advent of Code.

Problem Description

First, the problem description (copied from the website):

Day 2: I Was Told There Would Be No Math

The elves are running low on wrapping paper, and so they need to submit an order for more. They have a list of the dimensions (length l, width w, and height h) of each present, and only want to order exactly as much as they need.

Fortunately, every present is a box (a perfect right rectangular prism), which makes calculating the required wrapping paper for each gift a little easier: find the surface area of the box, which is 2 x l x w + 2 x w x h + 2 x h x l. The elves also need a little extra paper for each present: the area of the smallest side.

For example:

  • A present with dimensions 2x3x4 requires 2 x 6 + 2 x 12 + 2 x 8 = 52 square feet of wrapping paper plus 6 square feet of slack, for a total of 58 square feet.
  • A present with dimensions 1x1x10 requires 2 x 1 + 2 x 10 + 2 x 10 = 42 square feet of wrapping paper plus 1 square foot of slack, for a total of 43 square feet.

All numbers in the elves’ list are in feet. How many total square feet of wrapping paper should they order?


Once again, we’ll be working in Scheme.

For this problem, I decided to create a “box” data type. In addition to the automatically generated accessors (thanks SRFI-9!), I wrote several procedures to perform calculations on boxes, namely:

  • SURFACE-AREA: Calculate the box’s surface area.
  • SMALLEST-SIDE: Determine which of the box’s sides has the smallest surface area (the extra material makes it easier to wrap).

WRAPPING-PAPER is just a “wrapper” (pun intended) around the first two.

LINE->BOX, READ-BOXES, and SUM-BOXES are all about parsing the input file contents and shuffling them into the box data type that we use to do the actual calculation. The only part that required a bit of thought was the line with STRING-TOKENIZE in LINE->BOX. In Perl I’d use my @params = split /x/, $line without even thinking, but I was less familiar with Scheme’s facility for solving this problem, so it took a few minutes to puzzle out the right part of Scheme’s “API”. (STRING-TOKENIZE was helpfully provided by SRFI-13.)

Abstract data types FTW! I’ll be using them more as the month’s challenges progress.

;; ,open srfi-9 srfi-13 sort

(define-record-type box
  (make-box l w h)
  (l box-length set-box-length!)
  (w box-width set-box-width!)
  (h box-height set-box-height!))

(define (surface-area box)
  ;; Box -> Int
  (let ((l (box-length box))
    (w (box-width box))
    (h (box-height box)))
    (+ (* 2 l w)
       (* 2 w h)
       (* 2 h l))))

(define (smallest-side box)
  ;; Box -> Int
  (define (smallest-two xs)
    ;; List -> List
    (let ((sorted (sort-list xs <)))
      (list (first sorted)
        (second sorted))))
  (let ((l (box-length box))
    (w (box-width box))
    (h (box-height box)))
    (apply * (smallest-two (list l w h)))))

(define (wrapping-paper box)
  ;; Box -> Int
  (let ((minimum (surface-area box))
    (extra (smallest-side box)))
    (+ minimum extra)))

(define (line->box line)
  ;; String -> Box
  (define (line->lon s)
    ;; String -> List<Number>
    (let ((xs (string-tokenize s (char-set-complement (char-set #\x)))))
      (map string->number xs)))
  (let* ((dims (line->lon line)))
    (let ((l (first dims))
      (w (second dims))
      (h (third dims)))
      (make-box l w h))))

(define (read-boxes file)
  ;; Pathname -> List<Box>
  (with-input-from-file file
    (lambda ()
      (let loop ((line (read-line))
         (ys '()))
    (if (eof-object? line)
         (cons (line->box line) ys)))))))

(define (sum-boxes boxes)
  ;; List<Box> -> Int
  (let ((xs (map wrapping-paper boxes)))
    (apply + xs)))

;; eof

Related Posts

Advent of Code, Day 1


Advent of Code is a site that provides a programming problem for every day in December leading up to Christmas.

I’ve become a little obsessed with it over the last few days, and thought I’d write up my results. So far I’ve been working in Scheme.

Here’s the Day 1 problem description:

Day 1: Not Quite Lisp

Santa was hoping for a white Christmas, but his weather machine’s “snow” function is powered by stars, and he’s fresh out! To save Christmas, he needs you to collect fifty stars by December 25th.

Collect stars by helping Santa solve puzzles. Two puzzles will be made available on each day in the advent calendar; the second puzzle is unlocked when you complete the first. Each puzzle grants one star. Good luck!

Here’s an easy puzzle to warm you up.

Santa is trying to deliver presents in a large apartment building, but he can’t find the right floor – the directions he got are a little confusing. He starts on the ground floor (floor 0) and then follows the instructions one character at a time.

An opening parenthesis, (, means he should go up one floor, and a closing parenthesis, ), means he should go down one floor.

The apartment building is very tall, and the basement is very deep; he will never find the top or bottom floors.

For example:

  • (()) and ()() both result in floor 0.
  • ((( and (()(()( both result in floor 3.
  • ))((((( also results in floor 3.
  • ()) and ))( both result in floor -1 (the first basement level).
  • ))) and )())()) both result in floor -3.

To what floor do the instructions take Santa?

The file of instructions looks something like this (but much larger):


Here’s the (reasonably straight-forward) Scheme code. It basically just iterates through the input file, bumping a counter up or down based on the type of paren read from the input port. The definitions of UP-FLOOR? and DOWN-FLOOR? weren’t really necessary, but they made the main procedure a little easier to read.

(define (up-floor? x)
  (char=? x #\())

(define (down-floor? x)
  (char=? x #\)))

(define (find-floor file)
  (with-input-from-file file
    (lambda ()
      (let loop ((floor-number 0)
         (char (read-char)))
    (if (eof-object? char)
        (loop (cond ((up-floor? char)
             (+ floor-number 1))
            ((down-floor? char)
             (- floor-number 1))
            (else floor-number))

(Image courtesy Strongpaper under a Creative Commons license.)

A Solution to the Five Weekends Problem in Common Lisp


I’ve been enjoying playing around with Rosetta Code problems lately. Here’s a solution I posted last week for the Five Weekends problem in Common Lisp:


  ;; Given a date, get the day of the week.  Adapted from

  (defun day-of-week (day month year)
          (encode-universal-time 0 0 0 day month year 0)

  (defparameter *long-months* '(1 3 5 7 8 10 12))

  (defun sundayp (day month year)
    (= (day-of-week day month year) 6))

  (defun ends-on-sunday-p (month year)
    (sundayp 31 month year))

  ;; We use the "long month that ends on Sunday" rule.
  (defun has-five-weekends-p (month year)
    (and (member month *long-months*)
             (ends-on-sunday-p month year)))

  ;; For the extra credit problem.
  (defun has-at-least-one-five-weekend-month-p (year)
    (let ((flag nil))
          (loop for month in *long-months* do
                   (if (has-five-weekends-p month year)
                           (setf flag t)))

  (defun solve-it ()
    (let ((good-months '())
                  (bad-years 0))
          (loop for year from 1900 to 2100 do
             ;; First form in the PROGN is for the extra credit.
                   (progn (unless (has-at-least-one-five-weekend-month-p year)
                                    (incf bad-years))
                                  (loop for month in *long-months* do
                                           (when (has-five-weekends-p month year)
                                             (push (list month year) good-months)))))
          (let ((len (length good-months)))
            (format t "~A months have five weekends.~%" len)
            (format t "First 5 months: ~A~%" (subseq good-months (- len 5) len))
            (format t "Last 5 months: ~A~%" (subseq good-months 0 5))
            (format t "Years without a five-weekend month: ~A~%" bad-years))))

(Image copyright gacabo under Creative Commons license.)

Reasons to Recommend Common Lisp

On Reddit, /u/rhabarba asks: Should I learn (Common) Lisp? Here’s my reply:

I am a technical writer and “hobbyist” programmer who does some unofficial programming at work to automate documentation things. To give you a sense of my language background, I enjoy programming in Perl, Python, Scheme, and Common Lisp.

Reasons to recommend Common Lisp include:

  • In many ways, it’s a superset of the other languages I mentioned: you can “script” with regular expression and filesystem support, and you can also write larger programs using built-in OO that’s still more advanced than the built-in Perl or Python stuff.
  • Multi-paradigm: you can write code in whatever style you want to solve your problem. You only use OO if you want to, you’re not forced by the language. Functional style is there for you, especially via libraries. You can also be imperative and bash as much state as you want. Almost any type of code you might want to write can be (probably has been?) written in Common Lisp. There’s a lot of room to grow as you learn more.
  • Dynamic, interactive, and inspectable: redefine almost anything on the fly at the REPL, watch those changes get picked up by the rest of your system as it runs. This is true for running web servers, whatever. The debugger and inspector available via SLIME in Emacs is also better than anything else I’ve used in other languages.
  • Multiple good implementations to choose from; there are several fast “native” compilers, several portable interpreter-based implementations. If you write portable code you can run on many different implementations, so you can mix and match depending on what’s easiest to install or use for your needs.
  • Quicklisp lets you install any of 1200* libraries, very easily. You’ve got all the basics: HTTP clients and servers, JSON, XML, and Markdown parsers, and lots more advanced stuff too.
  • 25 year old code that does advanced, weird, or just cool things still works fine. It just runs way faster now.
  • Good books: ‘Paradigms of Artificial Intelligence Programming’ by Peter Norvig, ‘Object-Oriented Programming in Common Lisp’ by Sonya Keene, and many others.

Finally, I have been able to write programs to do “harder” things in Lisp than I have in other languages, even though I’m not really a programmer or “engineer” or whatever title you prefer. I think the simple list data structure is a great way to bootstrap a prototype of your program, and it encourages data-structure thinking rather than “stringy” thinking as some languages do. That said, I use a lot of languages to get my work done, so it’s not a religion or anything.

Good luck!

Simple Network Search in Scheme


I’ve been having fun translating some of the code in Winston and Horn’s Lisp into Scheme. This book is amazing – clearly written, with lots of motivating examples and applications. As SICP is to language implementation, Lisp is to application development, with chapters covering constraint propagation, forward and backward chaining, simulation, object-oriented programming, and so on. And it does include the obligatory Lisp interpreter in one chapter, if you’re into that sort of thing.

In this installment, based on Chapter 19, we will look at some simple strategies for searching for a path between two nodes on a network (a graph). The network we’ll be using is shown in the diagram above.

Here’s the same network, represented as an alist where each CAR:CDR pair represents a NODE:NEIGHBORS relationship:

'((f e)
  (e b d f)
  (d s a e)
  (c b)
  (b a c e)
  (a s b d)
  (s a d))

The high-level strategy the authors use is to traverse the network, building up a list of partial paths. If a partial path ever reaches the point where it describes a full path between the two network nodes we’re after, we’ve been successful.

As with trees, we can do either a breadth-first or depth-first traversal. Here’s what the intermediate partial paths will look like for a breadth-first traversal that builds a path between nodes S and F:

(s a)
(s d)
(s a b)
(s a d)
(s d a)
(s d e)
(s a b c)
(s a b e)
(s a d e)
(s d a b)
(s d e b)
'(s d e f)

Based on that output, we can deduce that every time we visit a node, we want to extend our partial paths list with that node. Here’s one option – its only problem is that it will happily build circular paths that keep us from ever finding the node we want:

(define (%buggy-extend path)             ;builds circular paths
  (map (lambda (new-node) 
         (cons new-node path))
       (%get-neighbor (first path))))

(Incidentally, I’ve become fond of the convention whereby internal procedures that aren’t part of a public-facing API are prefixed with the `%’ character. This can be found in some parts of the MIT Scheme sources, and I believe it’s used in Racket as well. I’ve started writing lots of my procedures using this notation to remind me that the code I’m writing is not the real `API’, that the design will need more work, and that the current code is just a first draft. I’m using that convention here.)

Here’s a better version that checks if we’ve already visited the node before adding it to the partial paths list – as a debugging aid it prints out the current path before extending it:

(define (%extend path)
  (display (reverse path))
  (map (lambda (new-node)
         (cons new-node path))
       (filter (lambda (neighbor)
                 (not (member neighbor path)))
               (%get-neighbor (first path)))))

You may have noticed the %GET-NEIGHBOR procedure; it’s just part of some silly data structure bookkeeping code. Please feel free to deride me in the comments for my use of a global variable. What can I say? I’m Scheming like it’s 1988 over here! Here’s the boilerplate:

(define *neighbors* '())

(define (%add-neighbor! k v)
  (let ((new-neighbor (cons k v)))
    (set! *neighbors*
          (cons new-neighbor *neighbors*))))

(define (%get-neighbor k)
  (let ((val (assoc k *neighbors*)))
    (if val
        (cdr val)

(%add-neighbor! 's '(a d))
(%add-neighbor! 'a '(s b d))
(%add-neighbor! 'b '(a c e))
(%add-neighbor! 'c '(b))
(%add-neighbor! 'd '(s a e))
(%add-neighbor! 'e '(b d f))
(%add-neighbor! 'f '(e))

Now that we have our data structure and a way to extend our partial path list (non-circularly), we can write the main search procedure, %BREADTH-FIRST. The authors have a lovely way of explaining its operation:

BREADTH-FIRST is said to do a “breadth-first” search because it extends all partial paths out to uniform length before extending any to a greater length.

Here’s the code, translated to use a more Schemely, iterative named LET instead of the linear-recursive definition from the book 1:

(define (%breadth-first start finish network)
  (let ((queue (list (list start))))
    (let loop ((start start)
               (finish finish)
               (network network)
               (queue queue))
      (cond ((null? queue) '())                    ;Queue empty?
            ((equal? finish (first (first queue))) ;Finish found?
             (reverse (first queue)))              ;Return path.
             (loop start
                   finish               ;Try again.
                    (rest queue)
                    (extend (first queue))))))))) ;New paths in front.

(A better way to write this procedure would be to implement a generic internal search procedure that takes its `breadthiness’ or `depthiness’ as a parameter. We could then wrap it with nicer public-facing search procedures specific names.)

Meanwhile, back at the REPL… we remind ourselves of what *neighbors* actually looks like, and then we search for a path between the nodes S and F.

> *neighbors*
'((f e) (e b d f) (d s a e) (c b) (b a c e) (a s b d) (s a d))
> (%breadth-first 's 'f *neighbors*)
(s a)
(s d)
(s a b)
(s a d)
(s d a)
(s d e)
(s a b c)
(s a b e)
(s a d e)
(s d a b)
(s d e b)
'(s d e f)

What fun! I can almost imagine using a three-dimensional variant of these searches for a space wargame with wormhole travel! Except, you know, they’d need to be much faster and more skillfully implemented. There’s also the tiny requirement to write the surrounding game…


1 It shouldn’t need to be said, but: Of course the authors knew better; they were trying to hide that unnecessary complexity from the reader until later.

Edwin `dired-do-shell-command’ on files

Evaluate this code in your Edwin REPL and you’re one step closer to being able to use Edwin as your primary file manager. I’ve reimplemented the Emacs `dired-do-shell-command’ function as an Edwin command (note that it puts these definitions in the `edwin dired’ environment’):

(ge '(edwin dired))

(define (string-join xs)
  (list->string (concatenate
    (map (lambda (s) (string->list s)) xs))))

(define (shell-command-prompt prompt)
  (prompt-for-string prompt #f
                            'DEFAULT-TYPE 'INSERTED-DEFAULT
                            'HISTORY 'SHELL-COMMAND))

(define (pathname->string pathname)
  (uri->string (pathname->uri pathname)))

(define-command dired-do-shell-command
  "Run a shell command on the file or directory at the current point."
  (lambda ()
    (list (shell-command-prompt "Shell command on file: ")
  (lambda (command pathname)
    ((ref-command shell-command)
      (list command " "
            (pathname->string (dired-current-pathname)) " &")) #f)))

(define-key 'dired #\! 'dired-do-shell-command)

Reading Email from Edwin/Imail

After much consternation, poring over of manuals, and scouring of the Interwebs for a decent, working `stunnel.conf’, I am finally able to read my mail from Edwin. And yes, it is amazing.

I think it’s probably time for me to make some documentation contributions to MIT Scheme and stunnel both.

Call it one more stumbling block overcome on the road to “Full Edwin”.

For the record, here are a few things to note when setting up Edwin’s IMail program:

– Read the manual! It’s available in a lovely linked PDF at

– Don’t fiddle with the value of `imail-default-imap-server’. Instead, configure your `stunnel.conf’ as shown below.

– Even after reading the manual, you need to play around in IMail to learn how best to do things. (I’m still working on this, as I literally just got it set up this morning.)

Finally, here’s my working stunnel.conf:


debug = 7
output = /home/rml/stunnel.log


[IMAP (Gmail) Incoming]
client = yes
accept =
connect =

[SMTP (Gmail) Outgoing]
client = yes
accept =
connect =

Onward, aspiring Scheme wizards!

(Now to figure out how to send!)

Playing with Lisp Machines

I’ve recently discovered the joy of playing with Lisp Machines via There are lots of emulators ranging from the early CADR to the TI Explorer. So far I’ve been playing with the CADR the most. It’s kind of amazing to think about some of the features of this environment. You can debug *everything* in the system, and in fact I’ve been kind of living in the debugger as a way of learning how things work. There is also a networking program called `chaosd’ that lets you connect to your local machine’s files so you can edit them in Zmacs!

All in all, very cool. Now off to read the fine manual.