Next: , Previous: , Up: The language   [Contents][Index]

5.2 Crash course for Elisp users

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.

Interactive evaluation

To evaluate Lisp expressions interactively, or to enter “REPL” – read, eval, print loop – then call rep or sawfish-client:

$ rep # or
$ sawfish-client

These two look similar, but there’s a striking difference. The rep program starts a clean, new REPL, but in sawfish-client, 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:

(defvar my-var 'a-value "An example variable")

Do NOT use defvar unless it’s necessary. One common use in Sawfish of defvar is to declare a user option. If you need a variable inside of a module, use define:

;; 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.

Like Elisp, defvar does not override an existing value. (But define does.) Function defvar-setq is defvar + setq, so it declares a global variable, and sets the value. (It was previously called define-special-variable, but 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)))

   ⇒ outer
;; The let binding is newly established inside of bar,
;; and foo doesn't see it.

;; Difference to defvar.
(defvar a 'outer)
   ⇒ inner ;; The scope of a is 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."
;; In Elisp, (defun foo (x) x)

(define bar foo)
(bar 'baz)
  ⇒ baz

  ⇒ <#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 define 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.

Use #!optional and #!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 add-hook:

(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.

The symbol 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 in Scheme.

#f evaluates to (), and #t 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 mapc and mapcar are fast, like Emacs. But “tail-recursion” is better than a while 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.

Next: , Previous: , Up: The language   [Contents][Index]