RLisp - Lisp naturally embedded in Ruby

  • Thread starter Tomasz Wegrzanowski
  • Start date
T

Tomasz Wegrzanowski

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)
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)
 
J

John Carter

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

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!
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)


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 : (e-mail address removed)
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.
 
J

John Carter

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 : (e-mail address removed)
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.
 
W

William James

Tomasz said:
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).

Very interesting.
Here are some examples:

; Fib function
(defun fib (n)
(if (<= n 1)
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])

Please don't use "let" for this. "setq" or "define"
would be much better.

From Structure and Interpretation of Computer Programs:

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.

From the newLisp manual:

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 ])

From comp.lang.lisp:

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

Tomasz Wegrzanowski

; Configure the server
(let config [Hash new])

Please don't use "let" for this. "setq" or "define"
would be much better.

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
(http://www.paulgraham.com/ilc03.html):

(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
`(local
(let ',[args get 0] ,[args get 1])
$B!!(B ,@(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.
 
W

William James

Tomasz said:
; Configure the server
(let config [Hash new])

Please don't use "let" for this. "setq" or "define"
would be much better.

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

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
allowing?

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

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)))

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)
)
)
)

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

Tomasz Wegrzanowski

[...]

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
likes):
def veclen (x,y) {
def x2 = x * x; // This is immutable binding, not a variable
def y2 = y * y;
def z2 = x2 + y2;
sqrt(z2)
}
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.
(setq a 2) 2
(funcall (lambda () (setq a 3) a)) 3
3
(funcall (lambda () (setq b 3) b)) 3
b
3

In RLisp a would be 2 and b would be undefined after the functions return.
So calling it setq would be about as confusing.
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?

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

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 :)
 
O

Ola Bini

Tomasz said:
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 =.

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.
And let means different things in different Lisps.
Scheme's let is different from Arc's. So it's not that radical.

This is very much not true at all. Completely wrong, actually.
[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 :)

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

Tomasz Wegrzanowski

Personally, I'd prefer setf.

Is setf used like that in any Lisp ?
Your implementation of scope gives me a headache. ;-) Is it newLisp
inspired?

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.
BTW, did you consider adding hygienic macros?

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 ?
 
O

Ola Bini

Tomasz said:
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 ?

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

Tomasz Wegrzanowski

Which standard?

Revised 5 Report on the Algorithmic Language Scheme.
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).

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 :)
 
O

Ola Bini

Tomasz said:
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 :)

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

Chad Perrin

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!

More to the point, it seems to me that an opinion about something
shouldn't be formed (and acted upon) until one understands it.
 
O

Ola Bini

Christian said:
Does Common Lisp count? :)

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

Ola Bini

Chad said:
More to the point, it seems to me that an opinion about something
shouldn't be formed (and acted upon) until one understands it.

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

Elliot Temple

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 :)

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
http://www.curi.us/blog/
 
T

Tomasz Wegrzanowski

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 -
http://t-a-w.blogspot.com/2006/07/free-beer-for-first-10-cool-rlisp.html
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.
 
N

N Okia

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.
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,578
Members
45,052
Latest member
LucyCarper

Latest Threads

Top