This short course explains how Librep differs from Emacs lisp. It is a good way to go, because librep can be seen as Elisp + some good parts of Scheme. The author is NOT lisp-headed, so you may find it rather slack.
To evaluate Lisp expressions interactively, or to enter “REPL” –
read, eval, print loop – then call
$ rep # or $ sawfish-client
These two look similar, but there’s a striking difference. The
rep program starts a clean, new REPL, but in
each expression is sent to the Sawfish window manager, and evaluated
therein. In this way you can change the Sawfish’s options on the fly:
$ sawfish-client ... user> (setq reboot-command "my/reboot/command --no-broadcast")
But if you enter an infinite loop by mistake, it makes the window manager freeze.
If you’re an Emacs user, then you can use “sawfish-mode”, too.
If you type C-x C-e like “Lisp interaction” mode, it works
as a wrapper to
sawfish-client, and the expression is sent
to it. For more on Sawfish, read Sawfish manual.
The biggest difference from Elisp is that Librep has modules and lexical scope. Module will be explained later, so for the time being just remember that the module system exists.
Variables and functions always live in a module, unless it’s global,
or “special”, or “dynamically scoped”. In Elisp all variables are
global, but not in Rep. You can declare a global variable with
(defvar my-var 'a-value "An example variable")
Do NOT use
defvar unless it’s necessary. One common use in
defvar is to declare a user option. If you need a
variable inside of a module, use
;; A variable, not global. (define var-2 'value-2)
You can make visible this variable to other modules. This will be explained later in the section on modules.
defvar does not override an existing value. (But
define does.) Function
setq, so it declares a global variable, and sets the value.
(It was previously called
a new, better name is provided now.)
Now, an important point. It’s a common technique to change the value
of a variable temporarily with
let in Elisp. This works too
in Rep, but ONLY with global variable. Please see the next example:
(define a 'outer) ;; Declares a variable. (define (foo) a) ;; In elisp, (defun foo () a) Use `define' for both ;; variable and function declaration. (define (bar) (let ((a 'inner)) (foo))) (bar) ⇒ outer ;; The let binding is newly established inside of bar, ;; and foo doesn't see it. ;; Difference to defvar. (defvar a 'outer) (bar) ⇒ inner ;; The scope of
ais now dynamic.
This is the lexical scope.
There’s also the notion of “fluid” variables in Rep which is an intermediate between lexical and dynamic. In my personal opinion, fluids are not so much useful because Rep’s grammar is poor. But it is used in Sawfish, too. See See Fluid Variables.
You may be shocked by the following example:
(define (foo x) "Returns X." x) ;; In Elisp, (defun foo (x) x) (define bar foo) (bar 'baz) ⇒ baz bar ⇒ <#closure foo @ my-module> ;; More straight example ((lambda (a b c) (list c b a)) 1 100 10000) ⇒ (10000 100 1)
So not only variables and functions live in the same namespace in Rep,
but also functions can easily be passed to variables, and called. This
is called “Lisp-1”, like Scheme, while Elisp and Common lisp are
“Lisp-2”. It can be a mnemonic that that’s why you use
for both variable and function definition.
In Rep, a lambda does not evaluate to itself, but to a “closure”. A closure can be considered as a function itself, written in lisp, and associated to the module it belongs to. (And a “subr” is a function in C.) That’s why the above example worked.
For function definitions, use
define, like the above example.
The grammar is intuitive, where the definition looks the same as
invocation. Definitions with
define can be nested, so
you can easily write helper functions which are only visible to
the wrapping function.
#!rest, instead of
&. You can
also use named argument passing with
#!key. See See Lambda Expressions.
In Rep, you can’t
funcall symbols. It’s of frequent use for
hooks in Elisp. Instead, you can just pass a function to
(define (viewport-window-uniconified w) ...) (add-hook 'uniconify-window-hook viewport-window-uniconified)
This works by the same logic as the previous example where a function is set to a variable.
See See Informal Module Introduction. (In fact, it was originally written as a part of this section, but a good tutorial on modules was lacking, so I promoted it to the entire document’s tutorial.)
Many differences of Rep from Elisp are there, but in this section let us see only some of them.
nil is bound to () in Rep. So (eq nil ’nil) is
t in Elisp, but nil in Rep. () evaluates to itself in both, but not
#f evaluates to
t. These two
come from Scheme.
Rep does not have the notion of “command”, but Sawfish does. See See Commands in The Sawfish Manual.
If you loop over list elements, then
fast, like Emacs. But “tail-recursion” is better than a
loop if byte-compiled (see Compilation Tips.)
Hooks take functions, not function names, because Librep is Lisp-1, as
we have seen above. The function
add-hook always add an
instance of the passed function, even if there’s already one in that hook.