By this point I believe it’s clear that I am a big fan of Scheme. Lately I have seen a sharp rise of interest in Ruby in some virtual places I attend (#lisp-br, twitter, IM with coworkers etc.). I am sure Ruby is a fine language, even in spite of several complaints I hear from some of its users. After all, it has symbols, metaprogramming, blocks (a sort of closures), lambdas and more! I’d really take the time to learn it, if it wasn’t for Matz himself:
Some may say Ruby is a bad rip-off of Lisp or Smalltalk, and I admit that. But it is nicer to ordinary people.
Now that is a demotivator. But Ruby must be evolving, and that quote may be outdated. So I have thought of a question to the Ruby-lovers, that may help me make up my mind to take the time to learn it. One of the best Scheme macros is, in my opinion, and-let*, from the SRFI-2. How would one write the following in Ruby, a procedure that I wrote a couple of nights ago?
;; Tries to get session from current request (define (get-session request) (and-let* ((cookies (request-cookies request)) (p (assoc "session_id" cookies)) (sid-str (cdr p)) (sid (string->number sid-str)) ((integer? sid)) ((exact? sid)) (sp (assq sid *sessions*))) (cdr sp))) |
In the and-let* macro, the clauses are evaluated and the variables are bound sequentially, creating new scopes for the next expressions. If any of the forms evaluates to #f (false in Scheme), the computation is aborted, no other expressions are evaluated, and the whole form evaluates to #f. How would I write this in concise Ruby?
You mean non-ugly Ruby, right?
It’s actually deceptively simple:
def get_session(req)
cookies = request_cookies(req)
and p = cookies[:session_id]
and sid_str = p[1]
and sid_nr = sid_str.to_i
and sid.is_a?(Integer)
and sp = sessions[sid]
and sp[1]
end
Forgot to enclose in PRE tags. Just indent according to taste
There are no ‘common’ way for such task. Maybe a bit stupid idea, but you can use something like ParseTree (http://parsetree.rubyforge.org/ParseTree/) to implement this macro in a way you would like it. Implementation won’t take more than 5-6 lines.
I would say any language with logical shortcut evaluation can get away with something like
function get_session(req)
local cookies = request_cookies(req)
local p = cookies and cookies.session_id
local sid_str = p and p[1]
local sid_nr = sid_str and tonumber(sid_str)
…
But I agree the Scheme version (or even the Ruby one) is more to the point.
[...] I came across Ventonegro’s post on and-let* and it set me thinking. The Lisp family of languages (which includes Scheme) are renowned for their [...]
@Bogdan:
It’s nice to see Lua here! But you would have to intersperse the lines with “if not x then return false end”…
@Peter:
The assignment inside the “and” is a nice trick, I am really surprised. Nevertheless, the scope of the variables is the whole function, and there are a lot of “ands”!
Hello, Maybe monad!
A Haskell version looks like this:
get-session request = do
cookies <- requestCookies request
p <- lookup “session_id” cookies
sidStr <- snd p
sid <- (readMaybe sidStr :: Maybe Integer) — readMaybe is not in the standard library for some reason, but is easy to write
guard (exact? sid) — not really, no exact/inexact distiction
sp <- lookup sid sessions
return (snd sp)
Indent all lines the same amount, except the first one.
[...] example of control operator is the very useful and-let* macro, semi-standardised in SRFI-2, tests several expressions in sequence, aborting the computation [...]