26 comments

[ 1.7 ms ] story [ 51.5 ms ] thread
That is actually Lisp influence on Smalltalk, and Perl, that eventually influenced Ruby.
Matz directly credits Lisp (through Emacs Lisp) as influence in the design of Ruby and its runtime, with Smalltalk influence on the language itself, and IIRC Perl as "what was popular and we tried to replace"
The article doesn't mention Perl at all but it did have some direct influences on Ruby.

Even Ruby's trailing blocks syntax are an homage to Perl's block list subroutines:

  # first Ruby example in article
  users.select { |u| u.admin? }.map(&:email)

  # using Perl's block list
  map {$_->email} grep {$_->is_admin} @users;
I love Ruby, use it for most of my projects that don't require performance.

Nothing I would love more than a Ruby with a Common-Lisp like compiler and runtime. Unboxed types, native compilation, partial compilation, live image (Ruby has this but "faster Rubies" like Crystal don't), etc...

I came close to adopting Scala, many parallels to Ruby with vastly better performance.

I'm Ruby or Lean 4.

I have a (self-hosted, but buggy and wildly incomplete; don't try to use - jRuby or TruffleRuby are better - and far faster - options) Ruby compiler that was partly born out of wanting to figure out what this would take, and the answer is it is massively painful because Ruby has failed to take some basic steps that makes delineating read-time and run-time very hard (e.g. you have fun patterns like overriding "require", and iterating over directories to decide what to require) even though most Ruby programs do have clearly separate load and run phases. It's just hard to programmatically separate it.

I still believe you could do pretty well there with a few basic "tricks" that could still also remain real/valid Ruby, by recognising the most common patterns, documenting them, and providing a way of marking exceptions. Combine that with freezing system classes after startup as an enabler for various optimization, and a compiler could do a pretty good job. But it's a massive piece of work to get it right for Ruby.

> He’s described Ruby’s design as starting from a simple Lisp, stripping out macros and s-expressions

Put the macros back! It would be so cool!

Put the s-expressions back too.
Macros depend on homoiconicity which Ruby sacrificed in order to have familiar syntax.
For folks that want all of this plus macros (and a lot of other great things), check out Elixir.
Elixir has forever ruined me for other languages. Every new PL I dip my toe into gets measured against it. Jose and the core team seem to always land on the right decisions, or at least very good ones.
Now that I'm out of the corporate tyranny and have my own company, I use lisp for everything. There's certain satisfaction in writing config files and persisting data directly in s-expressions. Any json requirements are triggered by exports to foreign systems.
That JSON prohibits trailing commata makes it an absolute pain to work with in practice.

I also like how in Haskell:

   something =
     { element
     , element1
     , element2
     , element3
     }
Is an actually idiomatic way to deal with the lack of trailing commata.
One way I find traditional Lisp style more painful for functional code than Ruby is that fully functional-style Lisp pushes me to read and write code the opposite way from how I think about it. In the author's example:

    orders
      .select { |o| o.placed_at > 1.week.ago }
      .group_by(&:customer_id)
      .transform_values { |group| group.sum(&:total) }
the equivalent Lisp code would either be written in imperative style as multiple statements that each write to a temporary variable or (let) binding, or would look like this:

    (reduce #'+
      (map (lambda (o) (getf o 'total))
        ; this group_by replacement function
        ; might be written as hash-table code
        (my-group-by 'customer-id
          (remove-if-not
            (lambda (o)
              (>
                (getf o 'placed-at)
                (- (my-now) (* 60 60 24 7))))
            orders))))
where I now have to read from bottom to top to understand the order of operations on the `orders` record set, even though when I wrote the code earlier, I "logically" thought from first operation to last when deciding which high-level operations to use in which order.

Other imperative languages that support functional code either make you do things imperatively to get the "logical" ordering of functional operations like I feel Lisp pushes you to do, or they do something like Ruby where things can be chained left to right in a "single" statement even for operations that were not thought of ahead of time by the creators of opaque data structures you later need to operate on. (Everything is a user-extensible object like Ruby, unified function call syntax in D, extension methods in C#, or pipelines of structured objects in PowerShell.)

I feel languages should just have some kind of sugar or operator for this, in fact in Ocaml the |> operator exists where

   <exp> |> <exp2>
   <exp2>(<exp>)
Are just one and the same

For a variadic language you'd need something more involved though. But some kind of syntax can probably be invented in some language.

It could just be written like:

  (~> orders
    (filter (lambda (order)
              (timestamp> (order-date order)
                          (timestamp- (now) 7 :days))))
    (group-by #'order-customer-id)
    (mapcar (lambda (group)
              (reduce #'+ group :key #'order-total)))
But I prefer the typical Lisp code where I get the sums of the totals of the orders with the same customer ID which were placed in the past week, instead of the orders made the past week grouped by customer ID their totals summed together.
(comment deleted)
was this before or after Lisp's epiphany for lexical binding?
I've never been more thoroughly convinced that I would like ruby more than from this article. I'm currently stuck reaching for python a lot of the time (absolutely love it tbc), but maybe it's worth changing things up and trying to give ruby a shot.

It was one of the first programming languages I was introduced to at 16 or so, but an older person that I looked up to told me it would get me stuck in "hobby coder land". He was wrong in so many ways, but even if he was right, I wanna have fun in my hobby code :)

> Both marks come from Scheme, where [...]

Reminds me of an email I wish I still had.

Circa 2000, I wrote that I was leaning towards moving to Scheme, for more rapid R&D work than I could do in Java.

Some nice-sounding person I didn't know emailed me from Japan, to mention a language I hadn't heard of, called Ruby.

I don't know whether the person was Yukihiro Matsumoto himself, but it's a small world.

As I read this, all of my favorite things about ruby compared to something like python were influenced by lisp. Ruby is a joy to program and it seems mostly due to design influence from lisp.
Most of the points listed are hardly considered lispy anymore these days, Python also has most of these.

Where Ruby's lisp lineage really shows is the fact that it's got Kernel#callcc, aka call with current continuation. It doesn't get any lispier than that!