
Why Ruby is an acceptable Lisp (2005) - behnamoh
http://www.randomhacks.net/2005/12/03/why-ruby-is-an-acceptable-lisp/
======
ekidd
Author here. Let me explain where this article came from, since people keep
digging it up every few years to discuss it. :-)

I spent some time during the late 90s learning all kinds of cool ways to use
Lisp, courtesy of a couple Boston startups, including IS Robotics (now better
known as iRobot). And I'd helped introduce Scheme to another employer. Scheme
made it ridiculously easy to create domain-specific languages, but it was
obviously a niche technology.

Around the same time, I'd heard Matz speak to a room full of Lisp hackers at
the Lightweight Languages conference. He'd elegantly explained why Ruby was
cool several years before Rails became widespread. But I don't think many of
us understood what he was saying until later.

When I finally realized—thanks to Rails—that Ruby could mimic many of the most
common Lisp metaprogramming tricks, I mentally kicked myself and wrote this
blog post. There were a lot of great responses from other bloggers, too. My
goal was to help make other people aware of some fun new possibilities.

Anyway, if anybody has any questions, I'll try to answer them later this
evening when I'm back online. It's fun to remember when this stuff was so new.

~~~
contingencies
Interesting article and thanks for chiming back in. I also discovered and
enjoyed the metaprogramming features of _ruby_ perhaps 5+ years ago. As I've
been learning it recently, can I ask if you have any particular take on _lua_?
To me, it feels a bit paired back, more purist, smaller core/libraries than
_ruby_ , but also drawing from the same tradition - ie. _lua_ 's
tables/functions equate well to lisp's lists/S-expressions (though practically
largely equivalent and including metaprogramming support) - despite not being
directly derived from a lisp.

~~~
ekidd
Lua looks like a nice language, but I've never really tried it. The Lua JITs
are really impressive.

Metaprogramming is a pretty widespread feature these days. Even Rust, for
example, has both Scheme-style rewrite-rule macros, and (unstable)
programmatic macros, for example.

------
KeatonDunsford
For a 2005 article, this is totally awesome, as well as that Steve Yegge
response. But in 2016, it's hard for me not to get excited about what's
seeming like Clojure's upward trajectory into the mainstream and want to jump
on board 100%. To paraphrase Rich Hickey, Lisp and immutable functional
programming totally rock, and the JVM and Javascript just reach
(ClojureScript). As our systems get bigger and more quality is demanded from
our software, it'd be so cool if we could get behind this as a standard, in
all the growing technology fields -- web and mobile (Om.next for React and
React Native), big data, AI/machine learning, Dockerization/containerization
and even decentralized stuff (Pelle Braendgaard's Cloth library for Ethereum
is a start, and I was just talking with some awesome people in the Clojure
Slack channel working on Boot tasks for IPFS, and soon the Golem Network
project will be launched which will be super dope). Hell, even designers can
start designing in Clojure now. It's just Simple, and as the community grows,
rallying behind it is only going to get Easier. People were raving with React
Native about how JavaScript developers could now all the sudden start coding
mobile apps. How cool would it be if there was just one awesome language, the
one true immutable functional Lisp, where everybody in tech could understand
each other and collaborate? I genuinely think this is Lisp's revenge (shoutout
David Nolen!).

It's been tough for me as a new developer to learn this stuff without nearly
as many resources as there are in the Python and Ruby communities for
beginners, but there's no question this is the (near-term) future. Plus, a lot
of that stuff just complects everything anyways and doesn't have to apply
anymore (read: mutable state, no Value on Values).

Let's keep it Lispy. ;)

~~~
qwertyuiop924
...No. Clojure _is_ lisp, but it's a weird little language, and most of us
lispers schemers aren't really totally happy with it. We're used to more
flexability, less functionalism, and less opinionation. Plus, Clojure's
equality and conses don't work at all the way a lisper would expect, and the
conses are way less useful.

There's a reason there will never be a universal language.

~~~
DigitalJack
As a clojure fan, I agree about conses. It's rare to use something like that.

Personally I'm happy equality doesn't work like common lisp. See here for a
post explaining equality in CL:
[http://eli.thegreenplace.net/2004/08/08/equality-in-
lisp](http://eli.thegreenplace.net/2004/08/08/equality-in-lisp)

It's old and I don't use common lisp, so please correct me or the post if
needed.

I've tried to get into lisp numerous times. Clojure was the one that clicked
for me. I can't say if that's because of clojure or because it was just the
right time for me after N times trying to get lisp.

Every now and then I poke at a scheme or CL. Scheme seems do-able for me,
particularly Chicken Scheme, but I everytime I try CL, I come across what
seems like too many functions to do what it seems like one function should do.
Whenever I see something like SETQ, etc., that's a big turn off for me.

On the other hand, I really dislike Clojure startup times. Doesn't matter for
server/long running apps, but when you are debugging or iterating, it gets to
be a pain. There are some mitigations with their own set of issues.

Pixie and Hy have been brought to my attention. Pixie seems more or less
abandoned, but Hy looks interesting. I haven't been able to tell from the docs
if it has clojure datastructures (which are the best part of clojure), but
Pixie does. So maybe sometime I'll try to bring those over to Hy and see if
they get accepted.

~~~
nilved
How are conses in Clojure different from those in Scheme? Is it the
implementation? Because the API seems the exact same to me.

~~~
aidenn0
Not a Clojure developer but, IIRC, Clojure doesn't have conses, but rather the
function cons creates a seq. A Seq in Clojure is immutable and (usually) lazy,
which is nothing like what CONS makes in lisp (a mutable pair that is the
basis for building singly-linked lists).

Note that linked-lists are only a good data structure in fairly narrow
contexts (e.g. shared structure, certain types of mutation you can do). Since
Clojure is focused on limiting mutation, it makes sense for them to use Seqs
instead of linked-lists.

~~~
zodiac
Didn't racket also make the decision to make cons create immutable lists (and
using mcons for mutable lists)?

~~~
qwertyuiop924
But that's not what Clojure did. In clojure, cons cells aren't cons cells:
they're a cell that can point to a sequence of some type, and contain a
pointer to arbitrary data. They also do not make up lists, and have weird
equality rules.

Racket's conses are merely immutable.

------
cwyers
Steve Yegge's response:

[http://steve-yegge.blogspot.com/2006/04/lisp-is-not-
acceptab...](http://steve-yegge.blogspot.com/2006/04/lisp-is-not-acceptable-
lisp.html?m=1)

~~~
atombender
Yegge's predictions don't always come through, but he basically predicted
Clojure. (The "someone will Thorvalds Arc" bit.)

------
lispm
> The most common use of LISP macros is to avoid typing lambda quite so much

LispWorks

    
    
        CL-USER 1 > (let ((n-macros 0) (n-with-macros 0) (with-macros nil))
                      (do-all-symbols (s (values n-macros n-with-macros))
                        (when (macro-function s)
                          (incf n-macros)
                          (let ((name (symbol-name s)))
                            (when (and name
                                       (> (length name) 4)
                                       (string-equal name "WITH" :start1 0 :end1 4))
                              (incf n-with-macros)
                              (push s with-macros))))))
        1186  ; macros
        182   ; with- macros
    
    

Note also that

    
    
        (with-slots (a b)
            foo
          (+ a b))
    
    

is not the same as:

    
    
        (call-with-slots foo
                         '(a b)
                         (lambda (a b) (+ a b)))

------
kevin_thibedeau
Tcl is a better lisp than Ruby. Homoiconic. Yield and tailcall make it easy to
implement FP idioms. Macros available if needed.

~~~
mroll
Wish more people understood this about tcl

~~~
pmontra
Maybe this is meta (the wish) :-)

I remember tcl in the early 90s then it disappeared from my radar, mostly web
and mobile apps. Honest question: is it still relevant in some application
area?

~~~
qwertyuiop924
It's still great at what it was always great at: embedding, acting as a CLI,
and doing random grunt work. It's Lua's main competitor, but it can also go
toe-to-toe with python and ruby in terms of libraries in some areas (although
by no means all of them). In particular, the ease of construction for
graphical applications is arguably unmatched (IUP is probably the closest),
SQLite was built to work with it, metakit is handy as well in the DB area, it
has a great event loop, several OO models, solid unix integration, sold FFI
and excellent embedding (as you'd expect), Starkits, which means you can
distribute your whole TCL sourcetree as a single file, and a ton of other
stuff.

So while it's not as popular, relevancy isn't an issue.

~~~
mroll
This comment is a great representation of tcl's relevance, which is nice to
see because tcl is often dismissed nowadays by people who don't understand it

------
white-flame
1) "Ruby is a denser functional language than Lisp"

Lisp is a regular, explicit language. It does not strive for micro-density,
but rather tends to achieve macro-density by allowing more expressive
abstraction and transformation in the larger componentry of your projects. Its
regularity around parenthetical s-expressions is the source of its expressive
power.

Things like "(+ (aref x 3) 4)" are larger than "x[3]+4", but the operations
and source code representation in Lisp are directly accessible to matching and
transformation without having to go through extra rigmarole to deal with
classes of AST node objects or whatever. This makes the usefulness of these
features immediately accessible for everybody, and useful for quick utility,
not just major undertakings.

2) "Ruby gives you about 80% of what you want from macros"

I'm sorry, but this section is simply ignorant of what Lisp macros are for.
Simple syntactic sugar is obviously possible with macros, but certainly not
its major use. Providing abstract constructs that require custom code
generation, creating lower-level boilerplate (like auto-creation of
introspective data structures and customized support functions), and wrapping
custom behavior around inline scopes are the primary uses I've seen and used.
Ruby can do a little of it, but I see no "80% of what you want" there.

3) "Ruby's libraries, community, and momentum are good"

Quicklisp was created after this article was written, and has been a godsend
to the community. Lisp tends to develop a very roll-your-own culture because
of its quick accessibility of creating abstractions, but the ease at which you
can publish & reach for libraries with QL has brought a lot of cultural shift
towards reuse. There are a ton of both esoteric and practical libraries out
there, and common libraries for threading, networking, OS interaction, etc,
are the common case now (and actually have been for quite a while before
Quicklisp as well).

There's tons of interoperating flexibility because the language provides a
huge baseline (especially its object system), so it doesn't all need to lock
into an on-top-of-Lisp infrastructure that would constrain the programmer's
options. It's a good time to be in Common Lisp.

------
hyperion2010
I have read the point about hygienic macros before. I have been working with
racket and common lisp and I cannot tell you have annoying it is to be trying
to learn macros and continually having to fight racket to let me do what I
want. CL on the other hand seems to get out of the way and let me shoot myself
in the foot just fine. I'd say that from a learning standpoint hygienic macros
(ala define-syntax) are a roadblock because they introduce a whole bunch of
hidden state which is hard to build a mental model of until after you have
encountered regular macros.

------
steveklabnik
A classic. Should have (2005) in the title. Previously on HN:
[https://news.ycombinator.com/item?id=416589](https://news.ycombinator.com/item?id=416589)
and
[https://news.ycombinator.com/item?id=825809](https://news.ycombinator.com/item?id=825809)

And a follow-up:
[https://news.ycombinator.com/item?id=2279453](https://news.ycombinator.com/item?id=2279453)

~~~
behnamoh
I just checked your links and gotta admit, you're right. Although, still I
don't see why this has been posted "three" times before!

I think HN should add a URL checker in the submit page.

~~~
sillysaurus3
_I don 't see why this has been posted "three" times before_

Posts that are over a year old can be reposted. It's a way to get a fresh
perspective on ideas.

~~~
cardiffspaceman
Maybe an URL checker would _implicitly_ discourage reposts by warning about
them.

If the URL checker were to present itself as a helpful aid for reposts ("You
might want to use this title like the other 3 posters have used: X") it might
avoid that result.

------
nabla9
>But, for the sake of argument, I'd like to boil them down to two things:

> LISP is a dense functional language. > LISP has programmatic macros.

First of these is wrong and second is just picking something from feature list
without any concern of practicality.

When someone writes Lisp as LISP and speaks about functional programming, I
get the feeling that the author has not programmed with Lisp outside school
(or knows only Scheme). Using toy examples don't help.

There is also problem with treating Lisp as a single entity. It's like
Treating C++ and JavaScript as the same language because their syntax looks
similar to someone who is not actually programming with them.

There are at least two major "schools" of lisp. Traditional Lisp style with
Common Lisp as it's main representative, another being Emacs Lisp. The another
is Scheme & Clojure camp that goes towards different goals.

~~~
ekidd
> When someone writes Lisp as LISP and speaks about functional programming, I
> get the feeling that the author has not programmed with Lisp outside school
> (or knows only Scheme)

I've actually spent something like a dozen years writing Common Lisp and
Scheme macros for production software. But I try not to put the nastiest ones
in blog posts. :-)

In my experience, real-world Lisp was often a surprisingly functional
language, certainly by the standards of the day.

Based on working with real-life Lisp code bases at four different
organizations, I also stand by my description of what Lisp macros in
production software typically looked like in 2005. Sure, there was the
occasional elaborate macro that implemented something like Prolog in Lisp, and
it was glorious. But the actual day-to-day workhorse Lisp macros were less
flashy—most of them delayed evaluation, improved syntax, or performed simple
transformations.

Many of these things are extremely valuable, but you don't necessarily need
the full power of macros to implement them.

~~~
qwertyuiop924
Like I said: the featured I find myself giving thanks for the most in lisp is
1) never having to remember syntax, or remember exactly how it works in
ambiguous contexts, and 2) never having to figure out how to indent my code.

The big stuff is there when it matters, but the little stuff makes up the day-
to-day.

------
ufmace
I'm glad to see this, and even more interested that it was written in 2005.
I've read lots of rants about how awesome Lisp is supposed to be from Paul
Graham and Steve Yegge and all. But I never see anything significant being
written in it or using it, so I haven't felt all that tempted to try learning
it.

Meanwhile, I've been using Ruby for a while, and the metaprogramming
capabilities are pretty cool. It sounds a lot like what all of the Lisp
enthusiasts are saying is so awesome about Lisp to me. So I've always wondered
what it is that's so awesome about Lisp that Ruby doesn't already have.

~~~
qwertyuiop924
Macros, arbitrary syntax transformation in macros, and object system as
powerful as CLOS, and the ability to comfortably use almost any paradigm.

It's not always useful, but you'll be thankful when it is.

~~~
feelsboutlisps
> Macros

I don't want my teammates writing macros. When the whole programming community
constantly talks about situations within regular, more expressive languages
(with syntax) like Python et al, where teammates look at each others code and
are baffled ... I struggle to see a scenario where inventing syntax through
macros is a good idea, and won't make that situation so much worse.

> arbitrary syntax transformation in macros

See above.

> object system as powerful as CLOS

If I wanted object systems, there are plenty of options that don't involve so
many parens, and do involve far more expressive syntax than lisps offer.

> and the ability to comfortably use almost any paradigm

I kinda want my team to pick a set of known paradigms. This is not an
advantage. I've never used a modern programming language and thought 'this
lacks paradigms, if only I could let my team invent some, that none of us will
understand 6 months from now'.

> It's not always useful, but you'll be thankful when it is.

From what you've said, I won't. I'll just be upset my teammates invented their
own language with macros and whatever paradigms they chose to use that day.

The lisp 'trade-off' (homoiconicity over expressive syntax) just seems like
it's looking for power in the wrong places. Power like that is more often bad
than good.

~~~
_delirium
On large team Lisp projects, you don't have every individual person
arbitrarily invent their own idiosyncratic abstractions. The project as a
whole has a common set of abstractions that fit the domain, which are designed
and modified the way you would design the abstractions regardless of the
language and programming paradigm you're using. Macros are a language tool
that lets you build abstractions, in some cases imo allowing better
abstractions than would be possible without macros, most often ones simulated
somewhat clunkily with OO features (or something godawful like C++ template
metaprogramming). But if you have a large team project you still need an
actually functioning team, common abstractions, project management,
documentation, code review, the usual stuff. Successful large Lisp projects of
course do all that.

I don't see a meaningful difference here between Lisp and, say, C++. If you
have a dysfunctional team writing C++, full of cowboy programmers who invent
their own idiosyncratic abstractions without coordinating with anyone, the
fact that C++ doesn't have proper macros doesn't really meaningfully limit the
resulting damage. What stops it from happening in functional teams isn't that
C++ the language prevents it from happening, but that the team has some kind
of working process that allows them to successfully write code as a team.

~~~
feelsboutlisps
> Macros are a language tool that lets you build abstractions, in some cases
> imo allowing better abstractions than would be possible without macros

I guess 'better' is the operative word I question here. You're creating syntax
that no-one has seen before. How is that a good thing? Meanwhile in languages
like Python, with syntax built-in, people build large apps without ever doing
this, making their code far more understandable for the next person that
touches it.

> I don't see a meaningful difference here between Lisp and, say, C++.

Agree :) (joke)

> If you have a dysfunctional team writing C++, full of cowboy programmers

At no point am I suggesting we can save ourselves from cowboys ...

> What stops it from happening in functional teams isn't that [...] the
> language prevents it from happening, but that the team has some kind of
> working process that allows them to successfully write code as a team.

But people are arguing 'the whole point' of lisps is that you _can_ be free to
do this, and that this justifies the 'paren-crazy', syntax-less language
trade-off, and I'm yet to see any justification it's necessary to allow this
at all.

What I'm saying is that all the lispy parens give you are footguns and all
you're saying is that good teams don't use those guns. Well then why give them
the guns at all. Why not give them a language with decent syntax instead.

~~~
ordu
> in languages like Python, with syntax built-in, people build large apps
> without ever doing this, making their code far more understandable for the
> next person that touches it.

No. Lack of macros makes code bigger, more error prone and harder to
understand.

When I use something like cl-ppcre:do-register-groups, this one word hides all
the loop orginization, and underlines meaning of local variables which are
bound to regex groups.

Or cl-sql:select, which allows programmer to use SQL syntax as part of lisp
while hiding special chars escaping, converting numbers to strings, ensuring
sql-syntax is correct.

Or simplier example: gl:with-primitives, which hides glBegin(...); ...
glEnd(). Note, it not just hides, it makes impossible to forget glEnd.

Macros makes life easier. Otherwise no one would use it. Macros such a
convinient way to make an abstractions, that switching from Lisp to any other
language becomes a great pain. One start to see tons of boilerplate code,
which needs to be written by hands, checked for errors with eyes, which hides
logic somewhere inside of boilerplate... It becomes better with practice, but
nevertheless lisp nostalgia will last forever.

~~~
feelsboutlisps
> No. Lack of macros makes code bigger, more error prone and harder to
> understand.

I mean, that's provably not true. Or at least not provably true. Let's me
throw out at alternative statement: Macros make code harder to understand.

> When I use something like cl-ppcre:do-register-groups, this one word hides
> all the loop orginization

You don't need a lisp to hide things.

> Or cl-sql:select, which allows programmer to use SQL syntax as part of lisp

Eugh, why would I want SQL interspersed with my lisp.

> [...] gl:with-primitives, which hides glBegin(...); [...] Note, it not just
> hides, it makes impossible to forget glEnd.

Python has 'with' statements and 'context managers' that do this (make sure
some cleanup happens). [1]

> Macros makes life easier [...] such a convinient way to make an abstractions

They might make life easier for the person writing them _in the short term_.
And I can see how when you find a really solid case for a macro and document
it well, open source it etc, it can be broadly useful, but then why not add
that case to the damn language. Instead you open the floodgates to everyone in
your team writing macros that aren't widely understood.

> Otherwise no one would use it.

A lot of people avoid them like the plague in all other languages.

> but nevertheless lisp nostalgia will last forever.

Maybe that's all it is though.

[1]
[https://docs.python.org/2/reference/compound_stmts.html#the-...](https://docs.python.org/2/reference/compound_stmts.html#the-
with-statement)

~~~
qwertyuiop924
So python's got a with statement. What of it didn't? Well then, you're
screwed. Hope you like calling file.close().

If _lisp_ didn't have a with statement, you could add one yourself. And if
_scheme_ didn't have a with statement... well, you could always add one, but
before dynamic-wind got added, it could break if you weren't careful. Some
things you can't just patch in. Anyways, now you'll say, "but python _does_
have a with statement!" But that was just an example: there are things python
will never have that you can add to lisp yourself. Like multimethod dispatch
and generics, anaphoric operators, lazy module loading, pattern matching,
ADTs, regex literals (with readtables), non-deterministic operators (the
classic amb/assert operators, for concisely expressing searching a list
(although they're admittedly more toys than anything else)), and a full-on
looping construct that allows for awk-style record processing of arbitrarily
typed records from arbitrary sources (field splitting is handled by other
functions). And that's just a few examples I came across. There are dozens of
others.

That would never all be added to the language: there's way too much, and it's
too specialized.

As for your theory that macros make code harder to understand, you don't have
any proof. In fact, you have negative proof, as there's plenty of logic, good
programming practice, and individual cases indicating that _careful_ use of
macros can make code easier to understand.

~~~
feelsboutlisps
> But that was just an example: there are things python will never have that
> you can add to lisp yourself.

I don't want to. I want a language that has sensible norms.

> As for your theory that macros make code harder to understand, you don't
> have any proof. In fact, you have negative proof,

Do I?

> as there's plenty of logic,

What logic? Show me. I simply don't believe you, as it's not provable.

> good programming practice

Still not proof. I believe the opposite, that it's not good programming
practice to use macros.

> and individual cases indicating that careful use of macros can make code
> easier to understand.

So anecdotal evidence, not proof.

You have no more proof than I have. My personal experience is that in _most_
mainstream languages, the most common arguments are over invention of
frameworks/libs vs using existing ones, and those devs certainly would not be
happy having macros thrown in the mix.

~~~
qwertyuiop924
Lisp has sensible norms: Unlike you, however, we acknowledge that what may be
a sensible norm for one situation isn't for another.

As for it not being good programming practice to use macros, it _isn 't_ good
practice to use them excessively. However, they can be used to make your code
much clearer. Here's the reasoning (no, it's not _proof_ , you're right on
that front, but it's better than what you've got, which is just your opinion,
AFAICT):

The larger a codebase grows, the more likely it becomes to contain bugs. The
most common way to decrease the size of the codebase is to abstract away
common idioms, only express them in one place, and reference that from other
parts of your program. This is what functions and objects do. However, some
idioms are incapable of being expressed as functions or objects. These idioms
must be repeated over and over again, and often result in insidious bugs when
somebody gets them subtly wrong. Macros allow you to capture those idioms and
express them once, eliminating bugs. They also make the code easier to
understand, by abstracting away complex logic.

So, if you really think macros are bad, than what's wrong with that line of
thinking?

~~~
feelsboutlisps
> Lisp has sensible norms

Again, in your opinion. And sorry to start blunt because I'm trying to round
this out as we're getting nowhere :)

To me, writing my code like it's an AST, amid a ton of parens is not sensible.
I've happily not done so in many other languages for over a decade, and I'm
very happy with that state of affairs (or at least I'm relatively happy with
the modern incarnations of C-style langs, e.g. Rust, Crystal, ES6, and I would
be very unhappy if I had to chuck that in tomorrow and write lisp).

Generally agree with your train of thought on abstractions, and see how you
arrive at macros. But my take is that it's simply not a good idea to go as far
as macros, especially when the syntax sacrifice is so great. If the language
is _truly_ missing something, take it up with the language authors.

(I've never encountered a situation in > 6 years of writing Python where I
encountered something I could not do and needed macros for).

Again, my basic opinion is I see it as a trade-off, and one that I don't agree
with. I think many others feel the same way if they get as far as
understanding the purported benefits of homoiconicity. But most aren't willing
to argue it on Hacker News and get downvoted to high hell (which is why I used
a throwaway).

(I should note meanwhile in another thread someone wrote off the entire JS
community as 'full of self-righteous assholes' [0] and got upvoted to top
post).

Thanks for the discussion, it was an interesting experiment in arguing these
thoughts I've had for a long time, as an 'abandoned' Clojure-curious dev. I'm
a bit sad I didn't get any new answers, but appreciate the insight all the
same.

[0]
[https://news.ycombinator.com/item?id=12482176](https://news.ycombinator.com/item?id=12482176)

~~~
qwertyuiop924
Ugh. I am always annoyed by people who hate on JS.

As for syntactic "sacrifices", they're necessary for the most complex macros,
not so much for the simple ones: rust has macros, too!

------
Dangeranger
Eric did a very good job predicting the direction of Ruby and Rails back in
2005, as well as the reasons why people chose them as tools over what existed
at the time.

------
RangerScience
The poingant guide link (implimentation of the "belongs_to" class function) is
a string of redirects. Anyone have a copy of the original?

~~~
pdkl95
[http://poignant.guide/book/chapter-6.html#section3](http://poignant.guide/book/chapter-6.html#section3)

edit:

While I _love_ _why's guide, which can be very useful for learning ruby
metaprogramming (and ruby in general), the technique presented there is a bit
outdated. Modern ruby doesn't require such an "ugly" style because we now have
Object#define_singleton_method[1] as a way of defining class level methods.
There shouldn't be a need to use #instance_eval to define methods in the
metaclass.

[1] [https://ruby-doc.org/core-2.2.2/Object.html#method-i-
define_...](https://ruby-doc.org/core-2.2.2/Object.html#method-i-
define_singleton_method)

------
pjmlp
I fail to see how it can be an acceptable Lisp when it lacks the AOT/JIT
compilation as part of the standard toolchain and the development productivity
of Common Lisp environments.

~~~
qwertyuiop924
I don't have the love of CL development environments you do, but yeah, as a
schemer, ruby's perf is kind of awful. We've got AOT, JIT, bytecode, and
literally decades of research spend making these languages fast, and Ruby
doesn't have that, or the hot code loading and quick iteration cycle that
makes Lisp/Scheme realtime development so deliciously sweet.

To say nothing of our macro superpowers. But then, CLers still insist on on
keeping the shotgun hovering over their foot, and insisting it's the only way
to go about writing macros. Syntactic Closures and Implicit Renaming are both
pretty cool. You guys should check them out.

~~~
pjmlp
I hardly have used Lisp besides Emacs Lisp and Clojure, but as a language geek
I do have my collection of Lisp related stuff, mainly Xerox PARC, Lisp
Machines and commercial CL environments.

Scheme also has environments like DrRaket, even if the language now has moved
beyond its Scheme origins.

~~~
qwertyuiop924
I don't know _how_ you got a lispm, but nice for you :-).

I was confusing you with Rainer (who, by coincedence, goes by the username
lispm on HN), who actually _loves_ CL environments.

As for me, I set my store by Geiser. Integrated documentation from all the
implmentations it supports, repl (as well as the capability to connect to a
remote one), support for all three big Schemes, is an emacs extension, and has
excellent docs? Consider me sold.

~~~
pjmlp
I wish.

I meant in what concerns papers, videos and magazines articles back from those
days.

~~~
qwertyuiop924
...Ah. Yeah, I wish I had a lispm too, but I don't even have the more
pedestrian antiques.

------
Grue3
This is a pretty bad article. The part that makes Lisp unique is hardly
lambdas (every language has them, even Javascript), or being functional, but
rather stuff like homoiconicity (and therefore macros). And once one scrolls
down to that part, it's full of ignorant statements like "the most common use
of LISP macros is to avoid typing lambda quite so much". It demonstrates a
fundamental misunderstanding of macros, because they're executed at the
compilation step, before the program is run. It's a zero-cost abstraction (wrt
performance). Not that anyone writing in Ruby cares about that.

Look at this example from the article:

(defmacro with-each-natural-number (n expr) `(each-natural-number (lambda (,n)
,expr)))

It assumes that the programmer already wrote a function each-natural-number
that does the required functionality when another function is passed into it.
That's the Ruby way, because it only has functions, not macros. A good Lisp
programmer would never write such inefficient code. Why create a local
function here when you can easily write a macro that expands into a loop with
expr as its body?

So, indeed, if you write in Lisp as if it were Ruby, then Ruby is an
acceptable Lisp. If you write in Lisp as it's supposed to be written, then
Ruby is ridiculously restrictive, and slow.

~~~
fiddlerwoaroof
Actually, that example is fairly idiomatic, it's called the "call-with" style.

From Google's Common Lisp style guide (
[https://google.github.io/styleguide/lispguide.xml#Macros](https://google.github.io/styleguide/lispguide.xml#Macros)
):

    
    
      You should follow the so-called CALL-WITH style when it
      applies. This style is explained at length in 
      http://random-state.net/log/3390120648.html. The general 
      principle is that the macro is strictly limited to 
      processing the syntax, and as much of the semantics as
      possible is kept in normal functions. Therefore, a macro
      WITH-FOO is often limited to generating a call to an
      auxiliary function CALL-WITH-FOO with arguments deduced
      from the macro arguments. Macro &body arguments are
      typically wrapped into a lambda expression of which they
      become the body, which is passed as one of the arguments of
      the auxiliary function.

~~~
lispm
> when it applies

One of the problems here is that the function does get an anonymous function
passed. That means the function can provide dynamic scope via constructs like
CATCH, LET with dynamic bindings, UNWIND-PROTECT, and so on. But it can't
rewrite any code in the passed function.

This can only be done in a macro.

~~~
fiddlerwoaroof
Fair enough, I was just responding to this:

> A good Lisp programmer would never write such inefficient code. Why create a
> local function here when you can easily write a macro that expands into a
> loop with expr as its body?

The nice thing about the call-with style is that it makes it easier to
redefine your macro's functionality without recompiling all the call sites.

------
Iispm
Ruby is the lispification of Perl, according to its creator.

------
LaPrometheus
No, It is not acceptable.

