Index

Interactive REPL

Suppose you try a new programming language. Newcomers often ask how to get started and which IDE to use. When talking about Lisp/Scheme, all you need are two computer programs: an evaluator and a text editor. The conventional approach is to write code in the text editor and load it in the evaluator. I believe this approach is popular because programmers first learn about it from compiled languages, such as C.

The issue here is that Lisp/Scheme can not only be compiled but interpreted at will. If all you do is write code in a text editor and load it into the evaluator, you do no better then as if you used a language that is compiled but not interpreted. One of many reasons as to why Lisp/Scheme is so powerful is because you can do (really?) everything at runtime, as well as at compile time.

So, my goal is to show writing programs in the Guile Scheme interpreter (REPL). More specifically, I will talk about adding new features to read, write, and edit expressions within the interpreter.

Editors and REPL

Let me refer back to the question of which IDE to use. In the case of Lisp/Scheme, the answer is often the almighty GNU Emacs, and the issue is that the steep learning curve is unwelcoming for beginners. One more issue is that an IDE is not a necessity for writing Lisp/Scheme. Again, all you need is a program to write expressions and an evaluator.

Nevertheless, I do advise you to learn an advanced editor like GNU Emacs, or its competitor Vim, both of which have great in-built tutorials easily accessible after starting either program. If you are unwelcome to the idea of using Emacs or Vim, you may find it easier to try GNU Nano. The reason I’m mentioning these editors is mostly that they are terminal based, which you will by the end of this chapter see is very important.

Now, an editor like Emacs or Vim is combined with an evaluator. The GNU Guile Scheme implementation comes with a computer program named guile that is an evaluator, interpreter, and compiler. But an evaluator is often simply called a REPL by Lisp/Scheme programmers.

Scheme defines three procedures that are very important for using the REPL:

In the absence of the optional port argument, the read is from the current-input-port, while the write is into the current-output-port. Note, that load is very different, as it reads and evaluates a whole file.

The conventional approach is to:

  1. write expressions in Emacs/Vim/Nano,
  2. save them in a file such as tmp.scm,
  3. and evaluate the file in the REPL, by using (load "tmp.scm").

Suppose tmp.scm contains:

(define a 1)
(display "Hello") (newline)
"result"

You run Guile and load tmp.scm:

> (load "tmp.scm")
Hello
$1 = "result"

It is as if you had written each expression individually:

> (define a 1)
> (display "Hello") (newline)
"Hello"
> "result"
$1 = "result"

Clearly, you can write expressions and, after evaluating them, save them into a file for later reevaluation. Thus, an alternative approach is to:

  1. write or read (from a file) uneavaluated expressions into the REPL,
  2. evaluate, edit, and iterate the expressions,
  3. save them in a file such as tmp.scm,
  4. and evaluate the file in the REPL, by using (load "tmp.scm").

What is more, Scheme allows you to redefine identifiers. For example (define a 1) followed by (define a 2) is allowed and results in binding the value 2 to the identifier a. This is helpful when it ocmes to evaluating and iterating expressions in the REPL.

It helps to define new procedures into the REPL to help with reading, writing, and editing expressions.

Using read and write, I will make iread and iwrite, where i is for interactive. The purpose of iread is to read a temporary file, which contains expressions, into memory. The purpose of iwrite is to save expressions from memory into a temporary file. This is one way a REPL can be intertwined with an editor.

One problem is that you can only move left and right when writing expressions in the REPL, as multi-line input is unsupported. One solution is to make iedit, which calls upon an external text editor to enter new multi-line expressions or edit existing ones.

What is more, it helps to add some additional modules into the REPL to help with writing expressions. So, I show how to do this next.

REPL value history

When writing an expression in the Guile REPL, you cannot use up and down arrow keys and movement keys home and end. That is, unless you use in-built modules to improve it.

The two important in-built modules are: (i) readline history and (ii) value history.

(i) Readline history takes expressions written into the REPL and saves them into a history file. Not only does it allow you to search this history file within the REPL, by pressing CTRL+r, but it also enables selecting previous expressions by pressing up and down arrow keys. To enable this, use the following expressions:

(use-modules (ice-9 readline))
(readline-set! history-length 2000)
(activate-readline)

The default history file is .guile_history. It can be changed by specifying an environment variable GUILE_HISTORY before running the guile program.

(ii) The REPL value history keeps the return value of evaluated expressions in memory. The values are bound to variables named with an $ following an integer, such as for example $1. As you evaluate many expressions, more than several hundreds, the evaluator may use a lot of memory. A procedure, named clear-value-history! and defined in the (ice-9 history) module, frees this memory. But the expression (clear-value-history!) is long. So, I prefer adding the following definition:

(use-modules (ice-9 history))
(define iclear (lambda () (clear-value-history!)))

One would expect that clearing the value history resets the result counter to $1, but that is not the case. See this example:

> 37
$1 = 37
> (clear-value-history!)
> 37
$2 = 37

So, I repeat myself here, for clarity:

One issue is that you would have to write these definitions every time you restart the guile REPL, but you can easily solve this with a .guile script. The .guile file is in the home directory and is executed before any other processing occurs. It is useful as a configuration for the REPL and is the perfect place to put your starting definitions. I will write line by line my .guile file for you, during this chapter.

Let us now see how to save and read expressions to write the .guile file from the REPL.

Interactive read/write

Here I show how to implement iread and iwrite.

Suppose you wish to write the above-needed history definitions from the REPL into the .guile file. These definitions can be saved directly into memory using the value history:

> '(use-modules (ice-9 history))
$1 = (use-modules (ice-9 history))

The second thing to note is that the value saved in memory can be written into a file (overwriting its contents):

> (call-with-output-file "tmp.scm" (lambda (port) (write $1 port)))

Now the tmp.scm file created contains the definition:

(use-modules (ice-9 history))

You can read this one expression with:

(call-with-input-file "tmp.scm" (lambda (port) (read port)))
$2 = (use-modules (ice-9 history))

This saves the value in memory without evaluating it. This data is a list, which you can confirm by checking with the list? predicate:

> (list? $2)
$3 = #t

This is great. The value is a list to which you can add new expressions. Here is an example:

> (cons $2 '(define iclear (lambda () (clear-value-history!))))
$4 = ((use-modules (ice-9 history)) define iclear (lambda () (clear-value-history!)))

Note that the result $4 is a list that contains two lists, one in the car and one in the cdr (note: not the cadr).

Writing and reading works exactly as before:

> (call-with-output-file "tmp.scm" (lambda (port) (write $4 port)))
> (call-with-input-file "tmp.scm" (lambda (port) (read port)))
$5 = ((use-modules (ice-9 history)) define iclear (lambda () (clear-value-history!)))

But it gets unfriendly as the number of expressions grows. Luckily, guile includes a pretty-print procedure:

> (use-modules (ice-9 pretty-print))
> (pretty-print $5)
((use-modules (ice-9 history))
 define
 iclear
 (lambda () (clear-value-history!)))

So, here is my approach to iwrite. Let _iwrite be a procedure that accepts a list of expressions, denoted by slist, and a port, denoted by port.

(define (_iwrite slist port)

The procedure _iwrite pretty-prints and newlines every element in the slist.

(define (_iwrite slist port)
  (begin
    (map (lambda (x) (pretty-print x port) (newline port))
         slist)
    #t))

The procedure iwrite calls _iwrite with a port with respect to the filename:

(define (iwrite slist filename)
  (call-with-output-file
    filename
    (lambda (port) (_iwrite slist port))))

This solves the writing part. Now we need to read the expressions. So, here is my approach to iread. Let _iread be a procedure that accepts a port, denoted by port:

(define (_iread port)

It includes a helper procedure, denoted by f, that accepts an expression, denoted by sexp, and includes a result, denoted by r:

(define (_iread port)
  (define (f sexp r)

The helper procedure returns the result when an end of file is reached:

(define (_iread port)
  (define (f sexp r)
    (cond ((eof-object? sexp) r)

Otherwise, it adds the expression that was read into the result list:

(define (_iread port)
  (define (f sexp r)
    (cond ((eof-object? sexp) r)
          (else (f (read port) (cons sexp r)))))

Now, _iread calls this helper procedure, but such that the result list is in the correct order:

(define (_iread port)
  (define (f sexp r)
    (cond ((eof-object? sexp) r)
          (else (f (read port) (cons sexp r)))))
  (reverse (f (read port) (list))))

This reads all the expressions in a file and is implemented trivially using tail-recursion. The procedure iread calls _iread with a port with respect to the filename:

(define (iread filename)
  (call-with-input-file filename _iread))

Writing .guile (1/4)

Let me now talk about writing the .guile file from the REPL. The needed procedure is iwrite, which you have to, first, evaluate, and second, write into the file. This is only but one of the expressions which are to be evaluated and saved. A simple way is to quote an expression to save it into the value history and then to evaluate it.

Suppose you evaluate a use-module expression directly with an eval expression:

> (eval '(use-modules (ice-9 readline)) (interaction-environment))

This is one example of a expression that can be evaluated with eval, but not all expressions are such. It is not possible to evaluate the expressions for defining iwrite and iread, as definitions are not allowed in eval with an interaction-environment as an argument.

To solve this problem, it is best to avoid eval. It is straightforward to see that a macro expansion can evaluate any expression and return the very same expression as the result.

Let eval-quote be a macro that accepts an expression, evaluates it, but returns not the evaluation result, but rather the expression itself:

(define-syntax eval-quote (lambda (x) (syntax-case x ()
  ((eval-quote sexp)
   #'(begin sexp (quote sexp))))))

You will need to write this macro twice: first to define it, and second to save it in memory.

> (define-syntax eval-quote (lambda (x) (syntax-case x () ((eval-quote sexp) #'(begin sexp (quote sexp))))))

> '(define-syntax eval-quote (lambda (x) (syntax-case x () ((eval-quote sexp) #'(begin sexp (quote sexp))))))
$1 = (define-syntax eval-quote (lambda (x) (syntax-case x () ((eval-quote sexp) #'(begin sexp (quote sexp))))))

Use the eval-quote macro to include readline and pretty-print.

> (eval-quote (use-modules (ice-9 readline)))
$2 = (use-modules (ice-9 readline))

> (eval-quote (readline-set! history-length 2000))
$3 = (readline-set! history-length 2000)

> (eval-quote (activate-readline))
$4 = (activate-readline)

> (eval-quote (use-modules (ice-9 history)))
$5 = (use-modules (ice-9 history))

> (eval-quote (define iclear (lambda () (clear-value-history!))))
$6 = (define iclear (lambda () (clear-value-history!)))

> (eval-quote (use-modules (ice-9 pretty-print)))
$7 = (use-modules (ice-9 pretty-print))

Use the eval-quote macro to define and quote _iwrite and iwrite.

> (eval-quote (define (_iwrite slist port) (begin (map (lambda (x) (pretty-print x port) (newline port)) slist) #t)))
$8 = (define (_iwrite slist port) (begin (map (lambda (x) (pretty-print x port) (newline port)) slist) #t))

> (eval-quote (define (iwrite slist filename) (call-with-output-file filename (lambda (port) (_iwrite slist port)))))
$9 = (define (iwrite slist filename) (call-with-output-file filename (lambda (port) (_iwrite slist port))))

Use the eval-quote macro to define and quote _iread and iread.

> (eval-quote (define (_iread port) (define (f sexp r) (cond ((eof-object? sexp) r) (else (f (read port) (cons sexp r))))) (reverse (f (read port) (list)))))
$10 = (define (_iread port) (define (f sexp r) (cond ((eof-object? sexp) r) (else (f (read port) (cons sexp r))))) (reverse (f (read port) (list))))

> (eval-quote (define (iread filename) (call-with-input-file filename _iread)))
$11 = (define (iread filename) (call-with-input-file filename _iread))

Write everything as the .guile script.

> (iwrite (list $1 $2 $3 $4 $5 $6 $7 $8 $9 $10 $11) ".guile")
$10 = #t

Restart guile, and it should automatically load the .guile script. You can now read the .guile script into the value history:

> (iread ".guile")
$1 = ((define-syntax eval-quote (lambda (x) (syntax-case x () ((eval-quote sexp) (syntax (begin sexp (quote sexp))))))) (use-modules (ice-9 readline)) (readline-set! history-length 2000) (activate-readline) (use-modules (ice-9 history)) (define iclear (lambda () (clear-value-history!))) (use-modules (ice-9 pretty-print)) (define (_iwrite slist port) (begin (map (lambda (x) (pretty-print x port) (newline port)) slist) #t)) (define (iwrite slist filename) (call-with-output-file filename (lambda (port) (_iwrite slist port)))) (define (_iread port) (define (f sexp r) (cond ((eof-object? sexp) r) (else (f (read port) (cons sexp r))))) (reverse (f (read port) (list)))) (define (iread filename) (call-with-input-file filename _iread)))

I believe you agree that the return value of the (iread ".guile") expression is extremely hard to read. I will now show iprint, which helps with reading such expressions.

Printing in the REPL

Evaluation results are saved in the value history of the REPL. Expressions themselves can be results, by quoting them. Expressions can be long, such that they take up many rows when printed in a terminal screen. To display them in a human readable way, I will implement an iprint procedure, such that each expression is assigned a number similar to how lines of code are numbered in a text editor.

So, iprint pairs each expression with a number, and this is done by converting a list into an association list, which is a list of pairs of a key and a value. The key (or the first element in the pair) is an integer. I call them nlists, as the keys are always numbers. Let list->nlist be a procedure that accepts a list, denoted by lst:

(define* (list->nlist lst #:optional (start 0))

A local variable, denoted by n, keeps track of the current integer index:

(define* (list->nlist lst #:optional (start 0))
  (let ((n (- start 1)))

Now map the list with a procedure that increments n and constructs a pair of n and the respective list-element:

(define* (list->nlist lst #:optional (start 0))
  (let ((n (- start 1)))
    (map (lambda (x) (set! n (+ n 1)) (cons n x)) lst)))

Note 1: the set! expression comes before cons because the return value of the cons is used for map. Note 2: the numbers are always increasing in steps of one.

You will also need to reverse this conversion. Let nlist->list be a procedure that accepts an association list and converts it into a list by dropping the key of every element:

(define (nlist->list nlist)
  (map (lambda (x) (cdr x)) nlist))

Often it is not only that you may wish to print all expressions, but a small part of them may be desired. It is the ilines procedure that I will define, that lets you select individual expressions with their expression number.

One approach to writing ilines is by using take and drop defined in:

(use-modules (srfi srfi-1))

Let ilines be a procedure that accepts a list of numbered expressions, denoted by nlist, an integer index, denoted by start, and the number of desired lines, denoted by num:

(define* (ilines slist #:optional (start 0) (num 0))

A local variable lst saves the list made by drop:

(define* (ilines slist #:optional (start 0) (num 0))
  (let ((lst (drop slist start)))

An if expression simply returns the above variable or further shortens the list with take:

(define* (ilines slist #:optional (start 0) (num 0))
  (let ((lst (drop slist start)))
    (if (zero? num) lst (take lst num))))

Let iprint be a procedure that accepts a list of expressions, denoted by slist, and the number of desired lines, denoted by num. It pretty-prints the result of ilines:

(define* (iprint slist #:optional (start 0) (num 0))
  (pretty-print
    (ilines (list->nlist slist) start num)))

Here is an example of printing the first three lines of the .guile script file:

> (iread ".guile")
$1 = ... 

> (iprint $1 0 3)
((0
  define-syntax
  eval-quote
  (lambda (x)
    (syntax-case x ()
      ((eval-quote sexp) (syntax (begin sexp 'sexp))))))
 (1 use-modules (ice-9 readline))
 (2 readline-set! history-length 2000))

Adding iprint to the existing .guile file is shown next.

Writing .guile (2/4)

Procedure iprint displays a list of expressions in a human-readable way, such that each expression is indented and numbered. The definitions for iprint need to be added to the .guile file, or else they are lost upon exiting the interpreter.

Like before, I will use the eval-quote macro to both evaluate expressions and save them in the value history. There are five expressions to add to the .guile file.

First is list->nlist as:

> (eval-quote (define* (list->nlist lst #:optional (start 0)) (let ((n (- start 1))) (map (lambda (x) (set! n (+ n 1)) (cons n x)) lst))))
$1 = (define* (list->nlist lst #:optional (start 0)) (let ((n (- start 1))) (map (lambda (x) (set! n (+ n 1)) (cons n x)) lst)))

Second is nlist->list as:

> (eval-quote (define (nlist->list nlist) (map (lambda (x) (cdr x)) nlist)))
$2 = (eval-quote (define (nlist->list nlist) (map (lambda (x) (cdr x)) nlist)))

Third is the use-modules as:

> (eval-quote (use-modules (srfi srfi-1)))
$3 = (use-modules (srfi srfi-1))

Fourth is ilines as:

> (eval-quote (define* (ilines slist #:optional (start 0) (num 0)) (let ((lst (drop slist start))) (if (zero? num) lst (take lst num)))))
$4 = (define* (ilines slist #:optional (start 0) (num 0)) (let ((lst (drop slist start))) (if (zero? num) lst (take lst num))))

Fifth and final is iprint as:

> (eval-quote (define* (iprint slist #:optional (start 0) (num 0)) (pretty-print (ilines (list->nlist slist) start num))))
$5 = (define* (iprint slist #:optional (start 0) (num 0)) (pretty-print (ilines (list->nlist slist) start num)))

Now each definition is in the value history, and to append it to a list, each must be in a list:

> (iwrite (append (iread ".guile")
          (list $1) (list $2) (list $3) (list $4) (list $5))
          ".guile")
$6 = #t

Note 1: iwrite will overwrite the existing .guile file, so before you run the command, make a backup in case you have errors.

Note 2: as an exercise, write a macro named listify of an arbitrary number of arguments. It returns a list where each element is a list that has exactly one element, the respective argument. So, (listify 1 2 3) expands into (list (list 1) (list 2) (list 3)).

Appending new expressions to an existing list of expressions is not a powerful editing operation. Next I will consider some other approaches to editing.

Interactive set, add, and del

While append proved useful for adding iprint, this is not always the case. Suppose you add an expression in the middle, or, you change an expression anywhere in the file. For such purposes, I shall now show iset, iadd, and idel.

Recall that iread makes a list and iprint displays it by numbering each expression starting at zero. Yet, lists in Scheme also start indexing at zero. It means the expression number aligns with the index. As iprint reveals this index, clearly, changing an expression is best done by selecting it by its index. This is why iset accepts an index argument, unlike searching by other means (equality comparisons).

Let iset be a procedure that accepts a list of expressions, denoted by slist, the index, denoted by index, and a new expression value, denoted by value:

(define (iset slist index value)

Setting an element in the list is the same as splitting the list into three parts: before the element, the element itself, and after the element. Changing then simply means to reconstruct the list, but with a new element.

One approach to splitting a list is to use split-at defined in:

(use-modules (srfi srfi-1))
> split-at
$1 = #<procedure split-at (lst i)>

Surprisingly, split-at returns two values:

> (split-at (list 0 1 2 3 4 5) 3)
$2 = (0 1 2)
$3 = (3 4 5)

In such a case, with two return values, a call-with-values procedure maps each value to a specific identifier:

> (call-with-values (split-at (list 0 1 2 3 4 5) 3)
                    (lambda (l r) l))
$4 = (0 1 2)
> (call-with-values (split-at (list 0 1 2 3 4 5) 3) 
                    (lambda (l r) r))
$4 = (3 4 5)

Here is how iset uses call-with-values and split-at to change the value of a list element at an index:

(define (iset slist index value)
  (call-with-values
    (lambda () (split-at slist index))
    (lambda (l r)
      (append l (list value) (cdr r)))))

In the same way, here is how iadd inserts a new value after an index:

(define (iadd slist index value)
  (call-with-values
    (lambda () (split-at slist (+ index 1)))
    (lambda (l r) (append l (list value) r))))

Finally, here is how idel removes a value with an index:

(define (idel slist index)
  (call-with-values
    (lambda () (split-at slist index))
    (lambda (l r) (append l (cdr r)))))

The following three examples illustrate use of the above-defined procedures:

> (iset (list 0 1 2 3 4) 2 0)
$5 = (0 1 0 3 4)

> (iadd (list 0 1 2 3 4) 2 0)
$6 = (0 1 2 0 3 4)

> (idel (list 0 1 2 3 4) 2)
$7 = (0 1 3 4)

Writing .guile (3/4)

Like before, I will use the eval-quote macro to both evaluate expressions and save them in the value history. There are three expressions to add to the .guile file.

First is iset as:

> (eval-quote (define (iset slist index value) (call-with-values (lambda () (split-at slist index)) (lambda (l r) (append l (list value) (cdr r))))))
$1 = (define (iset slist index value) (call-with-values (lambda () (split-at slist index)) (lambda (l r) (append l (list value) (cdr r)))))

Second is iadd as:

> (eval-quote (define (iadd slist index value) (call-with-values (lambda () (split-at slist (+ index 1))) (lambda (l r) (append l (list value) r)))))
$2 = (define (iadd slist index value) (call-with-values (lambda () (split-at slist (+ index 1))) (lambda (l r) (append l (list value) r))))

Third is idel as:

> (eval-quote (define (idel slist index) (call-with-values (lambda () (split-at slist index)) (lambda (l r) (append l (cdr r))))))
$3 = (define (idel slist index) (call-with-values (lambda () (split-at slist index)) (lambda (l r) (append l (cdr r)))))

Now that each new expression is in the value history, read the .guile file into memory:

> (iread ".guile")
$4 = ...

Check what the last expression number is:

> (iprint $4)
...
(15
 define*
 ...

Add the new expressions:

> (iadd $4 15 $1)
$5 = ...
> (iadd $5 16 $2)
$6 = ...
> (iadd $6 17 $3)
$7 = ...

Save the result:

> (iwrite $7 ".guile")
$8 = #t

Restart guile to check that each procedure has been added:

> iset
$1 = #<procedure iset (a b c)>
> iadd
$2 = #<procedure iadd (a b c)>
> idel
$3 = #<procedure idel (a b)>

Interactive edit

While iadd, iset, and idel are useful for editing single expressions, they are a poor choice when editing multiple expressions or multi-line expressions. For such purposes, I shall now show iedit.

Let iedit be a procedure of several optional arguments. It can be called with no arguments, where it defaults to editing an empty temporary file. It can be called with a list of expressions, by which it inserts these expressions into the temporary file and opens it for editing. Here is the definition:

(define* (iedit #:optional (slist (list)) (start 0) (num 0)
                #:key (editor "vim") (tmp "/tmp/iguile.scm"))

Procedure iedit writes the list of expressions into the temporary file:

(define* (iedit #:optional (slist (list)) (start 0) (num 0)
                #:key (editor "vim") (tmp "/tmp/iguile.scm"))
  (iwrite (ilines slist start num) tmp)

It uses a system call to launch an external text editor, defaulting to Vim:

(define* (iedit #:optional (slist (list)) (start 0) (num 0)
                #:key (editor "vim") (tmp "/tmp/iguile.scm"))
  (iwrite (ilines slist start num) tmp)
  (system (string-append editor " " tmp))

Finally, it reads the contents of the temporary file after editing:

(define* (iedit #:optional (slist (list)) (start 0) (num 0)
                #:key (editor "vim") (tmp "/tmp/iguile.scm"))
  (iwrite (ilines slist start num) tmp)
  (system (string-append editor " " tmp))
  (iread tmp))

The benefit here is in not reinventing the wheel, when it comes to editing code, but rather using professional editors like Vim/Emacs/Nano. For Emacs the editor string is “emacs –file”.

I also show two more procedure called isys and icls. The isys procedure uses a system call to run an external program and reads its output into the REPL value history. I will not describe how isys works, as it uses Guile’s open-pipe* procedure.

(use-modules (ice-9 rdelim) (ice-9 popen))

(define (isys mode prog . args)
  (when (null? args) (set! args ""))
  (define (_isys port)
    (define (f line r)
      (cond ((eof-object? (cdr line))
             (close-pipe port) r)
            (else
             (f (read-line port 'split) (cons (car line) r)))))
    (reverse (f (read-line port 'split) (list))))
  (case mode
    ((r)
     (_isys (open-pipe* OPEN_READ "/bin/sh" "-c" prog args)))
    ((w)
     (close-pipe
       (open-pipe* OPEN_WRITE "/bin/sh" "-c" prog args)))
    ((rw)
     (_isys (open-pipe* OPEN_BOTH "/bin/sh" "-c" prog args)))
    (else "invalid mode r/w/rw")))

One example to use isys is to clear the screen by running clear:

(define icls (lambda () (isys 'w "clear")))

Another example use of isys is to get the list of files with ls:

> (isys 'r "ls")
$1 = ("Desktop" "Documents" "Downloads" "Music" "Pictures" "Public" "Templates" "Videos")

Writing .guile (4/4)

Like before, I will use the eval-quote macro to both evaluate expressions and save them in the value history. There are four expressions to add to the .guile file.

First is iedit as:

> (eval-quote (define* (iedit #:optional (slist (list)) (start 0) (num 0) #:key (editor "vim") (tmp "/tmp/iguile.scm")) (iwrite (ilines slist start num) tmp) (system (string-append editor " " tmp)) (iread tmp)))
$1 = ...

Second is including the rdelim and popen modules as:

> (eval-quote (use-modules (ice-9 rdelim) (ice-9 popen)))
$2 = (use-modules (ice-9 rdelim) (ice-9 popen))

Third is isys as:

> (eval-quote (define (isys mode prog . args) (when (null? args) (set! args "")) (define (_isys port) (define (f line r) (cond ((eof-object? (cdr line)) (close-pipe port) r) (else (f (read-line port 'split) (cons (car line) r))))) (reverse (f (read-line port 'split) (list)))) (case mode ((r) (_isys (open-pipe* OPEN_READ "/bin/sh" "-c" prog args))) ((w) (close-pipe (open-pipe* OPEN_WRITE "/bin/sh" "-c" prog args))) ((rw) (_isys (open-pipe* OPEN_BOTH "/bin/sh" "-c" prog args))) (else "invalid mode r/w/rw"))))
$3 = (define (isys mode prog . args) ...

Fourth and final is icls as:

> (eval-quote (define icls (lambda () (isys 'w "clear"))))
$4 = (define icls (lambda () (isys 'w "clear")))

Now that each new expression is in the value history, read the .guile file into memory:

> (iread ".guile")
$5 = ...

Add the new expressions:

> (iadd $5 18 $1)
$6 = ...
> (iadd $6 19 $2)
$7 = ...
> (iadd $7 20 $3)
$8 = ...
> (iadd $8 21 $4)
$9 = ...

Save the result:

> (iwrite $9 ".guile")
$10 = #t

Interactive expressions

Here is a summary of the interactive expressions.

eval-quote sexp
iclear 
iread filename
iwrite slist filename
ilines slist start [num] 
list->nlist l [start] 
nlist->list nlist
iprint slist [start] [num]
iset slist index value
iadd slist index value
idel slist start 
isys mode prog . args
iedit slist [start] [num]
icls

Index