RLisp - Lisp naturally embedded in Ruby

Discussion in 'Ruby' started by Tomasz Wegrzanowski, Jul 24, 2006.

  1. Hello :)

    Maybe some of you guys will be interested.
    I wrote a small Lisp interpretter embedded in Ruby.
    The emdedding is very tight - Lisp macros are Ruby Proc objects
    and Lisp lists are Ruby Arrays (so actually cdr/cons copy).
    It is somewhat more Scheme-ish (or even Goo-ish) than Common Lisp-ish,
    but the macro system is more like Common Lisp's.
    [obj method args] is a reader macro for (send obj 'method args),
    which expands to obj.send:)method, *args).

    Here are some examples:

    ; Fib function
    (defun fib (n)
    (if (<= n 1)
    (+ (fib (- n 1)) (fib (- n 2)))
    (print (map fib '(1 2 3 4 5)))

    ; A small HTTP server
    (ruby-eval "require 'webrick'") ; import module
    (let HTTPServer (ruby-eval "WEBrick::HTTPServer")) ; bind class name

    ; Configure the server
    (let config [Hash new])
    [config set 'Port 1337]
    ; Tell the class to make us a server object
    (let server (send HTTPServer 'new config))

    ; Tell server to call our Hello World handler
    (send server 'mount_proc "/hello"
    (lambda (req res)
    [res body= "<html><body><h3>Hello, world!</h3></body></html>"]
    [res field_set "Content-Type" "text/html"]

    ; Tell the server to go !
    (send server 'start)

    Because it supports macros you can do things like:
    (def-server-html-mount server "/hello"
    (html (body (h3 "Macros greet you")))
    and that's pretty cool, because Ruby itself doesn't support macros
    and now you can use macros almost in Ruby :)

    The download: http://zabor.org/taw/rlisp/
    Documentation is only on my blog for now:
    * http://t-a-w.blogspot.com/2006/07/rlisp-lisp-naturally-embedded-in-ruby.html
    * http://t-a-w.blogspot.com/2006/07/rlisp-gets-basic-oo-support.html
    * http://t-a-w.blogspot.com/2006/07/rlisp-gets-http-support.html

    It uses Martin Traverso's Ruby port of ANTLR 3 for parsing.
    Well, that's pretty much all :) If you have any questions about it, just ask
    (on the mailing list, by private mail, or on the blog)
    Tomasz Wegrzanowski, Jul 24, 2006
    1. Advertisements

  2. Tomasz Wegrzanowski

    John Carter Guest

    This is just very very Cool.

    I love it!

    I'm not quite sure what (if anything) I will do with this, but Hey! The
    idea is Beautiful.

    Perhaps I will just make a cup of coffee, and pleasurable meditate on
    the existence of such a thing.

    Or perhaps hack on it until it outputs Joy instead of Lisp.

    Curiously enough my first response on seeing this was, "Wow! What an
    excellent thing to post on "Lambda The Ultimate"!"

    I see you beat me to it!

    Or maybe I will see if I can get around to finishing my recursive Lisp
    Parser for LittleLexer and drop that into your interpretor.

    Ah Well, somethings in Life are Just Fun.

    Thanks for Improving my Day!

    John Carter Phone : (64)(3) 358 6639
    Tait Electronics Fax : (64)(3) 359 4632
    PO Box 1645 Christchurch Email :
    New Zealand

    John's law :-

    All advances in computing have arisen through the creation of an
    additional level of indirection, the trick is to work out which
    indirection is actually useful.
    John Carter, Jul 24, 2006
    1. Advertisements

  3. Tomasz Wegrzanowski

    John Carter Guest

    On Mon, 24 Jul 2006, John Carter wrote:

    - Or perhaps hack on it until it outputs Joy instead of Lisp.
    + Or perhaps hack on it until it interprets Joy instead of Lisp.

    Foo! In my excitement I made a silly typo. Sorry.

    John Carter Phone : (64)(3) 358 6639
    Tait Electronics Fax : (64)(3) 359 4632
    PO Box 1645 Christchurch Email :
    New Zealand

    Carter's Clarification of Murphy's Law.

    "Things only ever go right so that they may go more spectacularly wrong later."

    From this principle, all of life and physics may be deduced.
    John Carter, Jul 24, 2006
  4. Great!

    Erik V.
    Erik Veenstra, Jul 24, 2006
  5. Very interesting.
    Please don't use "let" for this. "setq" or "define"
    would be much better.

    The first part of the let expression is a list of
    name-expression pairs. When the let is evaluated,
    each name is associated with the value of the corresponding
    expression. The body of the let is evaluated in a local
    environment that includes these names a local variables.
    No new mechanism is required in the interpreter in order to
    provide local variables. Let is simply syntactic sugar for
    the underlying lamda.

    syntax: (let ((sym1 exp-init1) [ (sym2 exp-init2) ...] ) body)
    syntax: (let (sym1 exp-init1 [sym2 exp-init2 ... ] ) body)

    One or more expressions in body are evaluated using the
    local definitions of sym1, sym2 etc. let is useful for
    breaking up complex expressions by defining local variables
    close to the place where they are used.
    The let form is just an optimized version and syntactic
    convenience for writing:

    ((lambda (sym1 [sym2 ...]) body ) exp-init1 [ exp-init2 ])

    (defun look-for-element ()
    (format t "~%this is the list we will work on: ~A~%" *list*)
    (let ((element (prompt-read-element)))
    (if (member element *list*)
    (give-position element *list*)
    (format nil "element not found"))))

    Emacs elisp:

    (let VARLIST BODY...): bind variables according to VARLIST
    then eval BODY. The value of the last form in BODY is
    returned. Each element of VARLIST is a symbol (which is
    bound to nil) or a list (SYMBOL VALUEFORM) (which binds
    SYMBOL to the value of VALUEFORM). All the VALUEFORMs are
    evalled before any symbols are bound.
    William James, Jul 24, 2006
  6. Now I know perfectly well that let is used in different way in other
    dialects of Lisp ;-), but I think this is way nicer. And it's less cryptic
    than setq (a big win); define is a bit too long, but the main problem is
    that it suggests global assignment, while (let x y) in RLisp rebinds
    x in local environment. Local environments are introduced
    by lambda/defsyntax, but I guess RLisp should also provide explicit
    (local ...)

    So you can compute let's say length of a vector using:
    (lambda (x y)
    (let x2 (* x x))
    (let y2 (* y y))
    (let z2 (+ x2 y2))
    (sqrt z2)

    I find it a lot more readable than Scheme's:
    (lambda (x y)
    (let (
    (x2 (* x x))
    (y2 (* y y))
    (let (
    (z2 (+ x2 y2))
    (sqrt z2)

    Even Lisp hardcorer Paul Graham seems to agree that let isn't very nice
    (althrough he's much less radical than RLisp), so in Arc you'll say

    (let x 'a
    (cons x 5)
    ; -> (A . 5)

    So in Arc the code would look like:
    (lambda (x y)
    (let x2 (* x x)
    (let y2 (* y y)
    (let z2 (+ x2 y2)
    (sqrt z2)
    What's already much better than Scheme, but still not perfect.

    And while I don't have much experience with Lisps, I have a lot experience
    with Objective Caml I really really totally *hate* let introducing
    extra indentation level
    and I think it obfuscates many otherwise clean programs for no real gain.

    I you can easily do have:

    ; No support for local yet, so let's emulate it
    (defsyntax local args
    `((lambda () ,@args))
    ; Arc-like let
    (defsyntax local-let args
    (let ',[args get 0] ,[args get 1])
      ,@(tl (tl args))

    And use local-let like Arc's if you really like it. Or even define Scheme-like
    one, but I think nobody's going to actually defend it against Arc's.
    Tomasz Wegrzanowski, Jul 24, 2006
  7. In newLisp, (setq x 9) is equivalent to
    (set 'x 9). The q is for 'quote'. So it's really not cryptic.
    'let' means 'allow', so it's quite cryptic; what are we

    With your 'let' you are doing something different than
    all other Lisps are doing with 'let', so you should give
    it a different name. By using the same name, you are
    creating confusion and making it more difficult to port
    code between other Lisps and yours. You are making
    it more certain that the only people who will use your
    Lisp are people who know no other Lisp. Is that
    what you want? Do you think that your Lisp is so
    magnificent that people will abandon all others and
    use only yours? If you would keep the semantics
    of your Lisp as similar as possible to the semantics
    of other Lisps, you would make it easier for others
    to learn it and, having learned it, to remember it.
    When Matz added features to Ruby from other
    languages, he tried to keep the original names.
    Consider Ruby's inject; it does the same thing
    as Smalltalk's inject. If it didn't, a Smalltalk programmer
    using Ruby would continually have to remember that
    he is dealing with a peculiar, nonstandard inject and
    not with the widely known, original inject.
    Changing the meaning of existing words may satisfy
    your inner urges, but it helps nobody else.
    You need 3 lets. newLisp needs only 1 letn.

    (define (vlen x y)
    (letn (
    x2 (* x x)
    y2 (* y y)
    z2 (+ x2 y2))
    (sqrt z2)))

    But that's not the best way to do it in Scheme.

    (define (vlen x y)
    (let* (
    (x2 (* x x))
    (y2 (* y y))
    (z2 (+ x2 y2)))
    (sqrt z2)))

    One let* does the work of three of your lets.
    William James, Jul 25, 2006
  8. [...]

    I have to say that I come more from Objective Caml/Ruby than
    from Scheme/Common Lisp, so I don't know much how Lisps
    are doing different things. I want is an operator "rebind in loca
    lexical environment" (global or the current lambda/defsyntax's),
    more or less like Ruby's = or Python's =.

    So the simple question I have is - what names does it have
    in existing Lisps, and what should it be called in RLisp ?

    Now why I called it let:

    In Objective Caml the most similar construct is called let:
    let veclen x y = (* this let changes global veclen *)
    let x2 = x *. x in (* this let operates is local, but is too lexical *)
    let y2 = y *. y in
    let z2 = x2 +. y2 in
    sqrt z2

    Now everybody hates its syntax and some people would much rather have it be:
    let veclen x y = (* this let changes global veclen *)
    let x2 = x *. x
    let y2 = y *. y
    let z2 = x2 +. y2
    sqrt z2

    That was the main reason why I used let.

    In Nemerle (which is ML inside, it just throw away ML syntax that nobody really
    def veclen (x,y) {
    def x2 = x * x; // This is immutable binding, not a variable
    def y2 = y * y;
    def z2 = x2 + y2;
    That's even more similar to RLisp's let, but def would confuse
    Ruby programmers too much (much more than let),
    as def is never used for local binding.

    setq in Common Lisp seems to have completely different semantics -
    it changes variable, not just rebinds it locally.

    In RLisp a would be 2 and b would be undefined after the functions return.
    So calling it setq would be about as confusing.
    RLisp is just-for-fun kind of Lisp, what lets me break all the rules.
    I did not want just another Scheme.

    And let means different things in different Lisps.
    Scheme's let is different from Arc's. So it's not that radical.
    I agree with the sentiment, but I don't see it as changing
    the meaning of existing word. I simply needed to name
    an important operation somehow. And please notice that
    in Ruby many things that are called the same as in different
    languages are completely different way, just in the
    most common cases happen to work like expected.
    Like Perl's scalar variables $<whatever> become global variables,
    what incidentally lets $_, $1 etc. work, and what was just brilliant.

    [about letn/let*]
    I know about let*, letrec and all the Scheme magic, but I find
    it very inelegant for a language to have multiple binding operators,
    so I tried to do with just one :)
    Tomasz Wegrzanowski, Jul 25, 2006
  9. Tomasz Wegrzanowski

    Ola Bini Guest

    Why don't you do an OCaml in Ruby instead, if you don't know the
    semantics of Lisp?

    The problem is that you're using let as a 'side-effect-operator' which
    it isn't. Let doesn't exist in Lisp. It's just a shortcut. This:

    (let ((a 1)
    (b 2))
    (* a b))

    is just a shortcut for

    ((lambda (a b) (* a b)) 1 2)

    So, you see, there's an excellent reason for let to have the semantics
    it have. Let is for establishing a NEW environment with different
    bindings. That's the main difference too what you're doing. Actually, as
    someone commented, your OCaml examble would look like this in lisp:

    (defun veclen (x y)
    (let ((x2 (* x x))
    (y2 (* y y)))
    (let ((z2 (+ x2 y2)))
    (sqrt z2))))

    which is very natural and _no_ assignment needed.
    The problem with your example from a lisp perspective is that it isn't
    obvious from your indentation or use that the let is scoped. And it can
    also appear anywhere, making it hard to reason about a program. With let
    you initialize all your bindings when the environment is established.
    It's just another way of doing it.

    Regarding setq and others,, what it does depends. If there's a local
    binding it will change that binding, if not, it will change the global one.
    This is very much not true at all. Completely wrong, actually.
    Hoho, that is _really_ wonderful. Scheme; inelegant? Because it has let*
    and letrec? Do you know the REASON those operators exist? No, didn't
    think so.

    Ola Bini (http://ola-bini.blogspot.com)
    JvYAML, RbYAML, JRuby and Jatha contributor
    System Developer, Karolinska Institutet (http://www.ki.se)
    OLogix Consulting (http://www.ologix.com)

    "Yields falsehood when quined" yields falsehood when quined.
    Ola Bini, Jul 25, 2006
  10. Is setf used like that in any Lisp ?
    I didn't even know newLisp, it's more Perl4-inspired :)
    And right now it doesn't work quite like I'd like it to, I plan to fix it.
    I couldn't understand them from the standard, and Paul Graham
    keeps saying that they're horrible, so I wasn't very inclined to try :)

    Can you show some examples where they are better ?
    Tomasz Wegrzanowski, Jul 25, 2006
  11. Tomasz Wegrzanowski

    Ola Bini Guest

    Which standard?

    Paul Graham obviously knows what he's talking about, but there is such
    things as differing opinions. Wasn't it you talking about breaking with
    convention? Break with Graham, I say!

    Hygienic macros remove some of the power of macros, but the power of
    real macros is easier to misuse and get subtle errors from. Are you used
    to real ones, hygienic suck. But they're better for average developers
    (which is way Graham doesn't like them).

    Ola Bini (http://ola-bini.blogspot.com)
    JvYAML, RbYAML, JRuby and Jatha contributor
    System Developer, Karolinska Institutet (http://www.ki.se)
    OLogix Consulting (http://www.ologix.com)

    "Yields falsehood when quined" yields falsehood when quined.
    Ola Bini, Jul 25, 2006
  12. Revised 5 Report on the Algorithmic Language Scheme.
    I agree with this sentiment, even if I often disagree with many of his opinions.

    But I haven't really seen any serious examples where one macro system
    is definitely better than another - like "we often want to do X, this is easy
    in Common Lisp, but Scheme disallows it" or "we often want to do Y,
    in Scheme we can do that directly, but in Common Lisp we have to use
    such and such hacks or it breaks". It would be nice if some Lispers
    described the issue for the rest of us :)
    Tomasz Wegrzanowski, Jul 25, 2006
  13. Tomasz Wegrzanowski

    Ola Bini Guest

    Actually, the reason people never say "we often want to do X, this is
    easy in Common Lisp, but Scheme disallows it" is that all Scheme
    implementations feature "real" macros outside the standard. Some things
    are not doable with only semantic extensions. Take a look at meeron for
    example; it _needs_ real macros to create an object oriented system
    inside Scheme.

    Ola Bini (http://ola-bini.blogspot.com)
    JvYAML, RbYAML, JRuby and Jatha contributor
    System Developer, Karolinska Institutet (http://www.ki.se)
    OLogix Consulting (http://www.ologix.com)

    "Yields falsehood when quined" yields falsehood when quined.
    Ola Bini, Jul 25, 2006
  14. Tomasz Wegrzanowski

    Chad Perrin Guest

    More to the point, it seems to me that an opinion about something
    shouldn't be formed (and acted upon) until one understands it.
    Chad Perrin, Jul 25, 2006
  15. Yeah, but setf changes variables in outside scope.
    (setf a 2)
    (defun foo ()
    (setf a 3)
    (foo) ; -> 3
    a ; -> 3

    I think it would be a huge win if variables were local to
    the nearest lambda (or equivalent) by default, just like in Ruby.
    Thanks, it is very interesting.
    Tomasz Wegrzanowski, Jul 25, 2006
  16. Tomasz Wegrzanowski

    Ola Bini Guest

    Heh, you seem to misread the question. The question referred to an
    example by the OP using setf in a very un-lisp way, and the OOP asked if
    setf actually could be used the way the first OP made an example of.

    Ola Bini (http://ola-bini.blogspot.com)
    JvYAML, RbYAML, JRuby and Jatha contributor
    System Developer, Karolinska Institutet (http://www.ki.se)
    OLogix Consulting (http://www.ologix.com)

    "Yields falsehood when quined" yields falsehood when quined.
    Ola Bini, Jul 25, 2006
  17. Tomasz Wegrzanowski

    Ola Bini Guest

    This was another point I tried to get across, but your statement
    captures my intention very succinct. +1!

    Ola Bini (http://ola-bini.blogspot.com)
    JvYAML, RbYAML, JRuby and Jatha contributor
    System Developer, Karolinska Institutet (http://www.ki.se)
    OLogix Consulting (http://www.ologix.com)

    "Yields falsehood when quined" yields falsehood when quined.
    Ola Bini, Jul 25, 2006
  18. Umm, where does he say that?

    I thought his view was that "real" macros are more important/powerful/
    useful, but hygienic ones are good to have around as backup.

    -- Elliot Temple
    Elliot Temple, Jul 26, 2006
  19. New version of RLisp is available. Now with lexical scoping,
    separate standard library, and free beer for 10 first people to
    write cool RLisp programs.

    Let's see how the design actually does in practice instead
    of endlessly discussing whether it's elegant or not.

    I guess it should be reasonably usable for playing with it by now.

    Documentation -
    Download - http://zabor.org/taw/rlisp/

    If you have any feedback (suggestions, bug reports, cool programs, rants)
    send it to my blog, email, or to the ruby-talk mailing list, whichever
    you prefer.
    Tomasz Wegrzanowski, Jul 26, 2006
  20. Tomasz Wegrzanowski

    N Okia Guest

    My thoughts, as obtuse as they ever are:

    1) I have really liked Ruby for the very reason that I can often
    express algorithms from scheme/lisp directly in Ruby, but with a much
    more readable syntax. Yes, This will be controversial to the lambda
    devotees. However, my point is that Ruby can do this far easier than
    languages like C or Java.

    2) The 'endless discussion' does seem to be a feature of learning to
    program in Lisp. I suppose this may be because people who program in
    Lisp give a lot of thought to details of both design and
    implementation of software. However, I suspect that it may be that
    Lisp attracts a certain amount of what we used to call 'rules lawyers'
    at the gaming table.
    N Okia, Jul 26, 2006
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.