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.
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:
read [port]
– read one expression from a port into memorywrite sexp [port]
– write expressions from memory into a portload filename
– read and evaluate expressions from a fileIn 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:
tmp.scm
,(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:
tmp.scm
,(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.
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.
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))
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.
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.
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.
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)
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)>
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")
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
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