The dark corners of rep



On May 12, Christopher Roy Bratusek wrote:
> Am Dienstag, den 12.05.2009, 13:58 +0900 schrieb Teika Kazura:
> > One more bad point is there: not for dev, but that users of
> > Sawfish have to learn Rep.
> 
> ... if we switch they'll have to learn <xyz> instead of rep, that
> not a valid point against rep in favour of lisp/scheme.

It is a valid point against rep (are there any books about rep? anyone
uses it outside of sawfish?).  Even more: it is a *major* point
against it.  Scheme and Lisp systems can be very flexible, and it's
easy to come up with a language that is scheme- or lisp-like (just
using sexpr syntax for that is enough).  If the semantics of the
language is far enough from scheme or lisp, then you still have a
different language, despite the superficial resemblance.

The thing with rep is that it's somewhere between Scheme, Lisp, and
ELisp.  This is a very bad thing -- more than just the lack of books
to learn it (I'm sure that most people just read about scheme or lisp
or elisp, but they're all different languages from rep).

Here's a list of some obvious problems I ran into.  I don't know if
there are many people here who use Scheme or Lisp, but those who do
will probably be horrified by these (and if they tried to program
sawfish, they probably know about some of them).

* No `set!'; uses `setq' and it can be used with new variables

    user> (set! x 1)
    *** Unbound variable: set!
    user> (setq x 1)
    1

  This is like Lisp and ELisp.  There's even `set', which comes from
  the dark past of Lisp:

    user> (setq x 'y)
    y
    user> (set x 123)
    123
    user> y
    123

  There is no `setf', so it looks like it'll be close to ELisp.

* However, these variables are lexically scoped -- not dynamically
  scoped as in Lisp/ELisp:

    user> (setq x 123)
    123
    user> (define (ret-x) x)
    user> (let ((x 999)) (ret-x))
    123

  This is actually a very important point -- rep is lexically scoped
  (which is good), which means that it has closures, and `lambda' is
  not just a self-evaluating form:

    user> (funcall '(lambda () 1))
    *** Invalid function: (lambda () 1)
    ;; this *does* work in elisp, and in some popular lisps too

* Actually this is another confusing point: rep has `funcall', but it
  is not necessary since it has a single namespace.  In fact, the
  implementation could just as well be in rep instead of in C:

    (define (funcall f . xs) (apply f xs))

  which is what the C code is doing.

* Also, rep has `defun' and `defvar', but the distinction between them
  in lisps is setting the "function value" or the "symbol value" --
  this is not something that rep has, so `defun' is mostly syntactic
  sugar for `define' with function.

* `defvar', however, still makes the defined name special (dynamically
  scoped):

    user> (defvar x 1)
    x
    user> (define (ret-x) x)
    user> (let ((x 123)) (ret-x))
    123

  This has the usual possible catastrophe:

    user> (define (foo x) (ret-x))
    user> (foo 5)
    5

  which Common Lisp users solve by always naming special variables
  with *stars* (and some compilers will warn you if not).

* Even worse, `defvar' does its magic in a way that shadows possible
  later definitions:

    user> (defvar x 123)
    x
    user> (define x 1234)
    user> x
    123
    user> (defun x () 12345)
    user> (x)
    *** Invalid function: 123

  (This behavior is unique to rep, AFAICT.)

* It has Common Lisp's idiom of using `defun' inside a `let' to get a
  local binding:

    user> (let ((count 0)) (defun count! () (setq count (1+ count))))
    user> (list (count!) (count!) (count!))
    (1 2 3)

  but -- it also does the same with `define':

    user> (let ((count 0)) (define (count!) (setq count (1+ count))))
    user> (list (count!) (count!) (count!))
    (1 2 3)

  which is of course not needed, because you can do the usual thing in
  scheme:

    (define count! (let ((count 0)) (lambda () (setq count (1+ count)))))

* But -- yes, there's another "but" -- it also does what Common Lisp
  is doing with multiple `defun's, and it will happily do so with
  `define':

    user> (let ((count 0))
            (define (count!) (setq count (1+ count)))
            (define (reset!) (setq count 0)))
    user> (list (count!) (count!) (reset!) (count!))
    (1 2 0 1)

  which happens because `define' is actually more like Lisp's `defvar'
  and `defun' in that it has a global effect (which is a surprising
  contradiction for a lexically scoped, single-namespace language):

    user> (let ((x 1)) (define xx (1+ x)) xx)
    2
    user> xx
    2

  *BUT* -- `define' *is* defining something local if it's in a
  function's scope:

    user> (define (foo x) (define xx (1+ x)) xx)
    user> (foo 5)
    6
    user> xx
    *** Unbound variable: xx

  which makes rep more like JavaScript.  Still `defvar' does have a
  global effect:

    user> (define (foo x) (defvar xx (1+ x)) xx)
    user> (foo 1)
    2
    user> xx
    2

  which is just like `setq':

    user> (define (foo x) (setq xx (1+ x)) xx)
    user> (foo 1)
    2
    user> xx
    2

  and let's not forget `set' -- unlike in modern Lisps (which are
  lexically scoped except for `defvar'ed bindings), `set' behaves as
  it does in a dynamically scoped language:

    user> (define (foo x) (define xx 'x) (set xx 33) x)
    user> (foo 1)
    33

  but `eval' behaves as a lexically scoped language:

    user> (define x 3)
    user> (let ((x 999)) (eval 'x))
    3

* There's also a bunch of elisp-isms that make the language appear
  more like elisp:

  - `concat', not `string-append'
  - `mapcar', not `map'
  - `?x' syntax for characters, not `#\x'
  - `defmacro' macros, no hygiene; `defmacro' syntax as in lisp
    (separate argument list)
  - "first class" macros (yet another can of worms) that can be passed
    around like values (which even elisp can't use directly).
  - `print' and `princ', not `write' and `display' (and `write' is
    bound to something different)
  - `integerp', `stringp', `string=' , etc -- not `integer?',
    `string?', `string=?', etc
  - doc strings
  - elisp-like vectors (bracket syntax, `length', `aref'), unlike
    scheme or common lisp
  - lisp/elisp-like backquotes (try ``,a and also `backquote' vs
    `quasiquote')

  and some scheme-isms:

  - `number->string', not `number-to-string'
  - `call/cc'
  - exact rationals

  and some of its own features (regexps, threads, IO, modules, and
  more).

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                  http://www.barzilay.org/                 Maze is Life!


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]