Lisp's Influence on Ruby

tacoda1 pts0 comments

Lisp’s Influence on Ruby. Once I wrote users.select { |u|… | by Ian Johnson | Jun, 2026 | MediumSitemapOpen in appSign up<br>Sign in

Medium Logo

Get app<br>Write

Search

Sign up<br>Sign in

Lisp’s Influence on Ruby

Ian Johnson

8 min read·<br>Just now

Listen

Share

Once I wrote users.select { |u| u.admin? }.map(&:email) and realized I’d written Lisp.<br>Not literally. The parentheses are gone, the prefix notation is gone, the lambdas are syntactic blocks. But the shape of the code (chain a filter onto a transform, ask each element a yes-or-no question with ?, build the result without mutating anything) is Lisp. Ruby just put it in business casual.<br>Matz has said as much. He’s described Ruby’s design as starting from a simple Lisp, stripping out macros and s-expressions, then adding an object system, blocks, and Smalltalk-style methods. The features most Rubyists fall in love with aren’t the object-oriented ones. They’re the functional ones, dressed in friendlier clothes.<br>Here is the list I think about often, and why each one matters.<br>Method names with question marks<br>The convention that predicates end in ? came from Scheme. zero?, nil?, empty?, respond_to?, valid?. The mark tells you, at a glance, that the method answers a yes-or-no question. It does not mutate. It does not perform an action. It tells you something true or false about the receiver.<br>return if user.nil?<br>return unless user.admin?<br>notify(user) if user.subscribed?You can read those three lines as English because the ?`?` does the heavy lifting. The same convention shows up as ! for methods that mutate or raise: save!, sort!, compact!. Both marks come from Scheme, where null?, pair?, and set! work the same way.<br>A small syntactic borrow, but it threads through the whole language. Reading Ruby is faster because of those two characters.<br>Closures and blocks<br>Blocks are the feature most Rubyists name first when asked what they love about the language. They’re closures: chunks of code that capture their surrounding scope and can be passed around as values.<br>total = 0<br>[1, 2, 3].each { |n| total += n }<br>total # => 6The block closes over total. That is the closure pattern: a function value that remembers the environment it was defined in. Lisp had closures decades before Ruby. Scheme made them first-class objects you could pass to anything. Ruby kept the idea and added the lighter syntax. A block, with do…end or curly braces, is a closure with the parentheses stripped off.<br>Procs and lambdas are the same idea with the parentheses back on:<br>square = ->(n) { n * n }<br>[1, 2, 3].map(&square) # => [1, 4, 9]That arrow syntax is Ruby’s lambda. The word itself is Lisp’s, from Church’s lambda calculus, plumbed into a working programming language for the first time in 1958.<br>First-class functions<br>Once you can name a closure and pass it around, functions become values. You can store them in arrays, return them from methods, attach them to objects. Ruby’s Method and Proc classes make this explicit. So does &:method_name, which converts a symbol into a block by looking up the method on the receiver.<br>emails = users.map(&:email)<br>admins = users.select(&:admin?)That &:foo is a small piece of magic, and it works because functions are values in Ruby. The symbol gets coerced into a proc, the proc gets passed as a block, the block gets called on each element. First-class functions all the way down.<br>This is Lisp’s foundational idea: programs are built by composing functions. Ruby borrows the composition and dresses it up in dot-chains.<br>Symbols<br>:foo is a symbol. It looks like a string with a colon, but it’s a different kind of value. Symbols are interned: every time you write :foo, you get the same object. Two strings that look the same are usually two separate objects in memory; two symbols that look the same are always one.<br>That property comes from Lisp. Lisp symbols (atoms, in some dialects) are the original interned values. The reader sees foo, looks it up in a symbol table, and either returns the existing symbol or creates a new one and remembers it. After that, all references to foo point to the same object.<br>:status.equal?(:status) # => true<br>"status".equal?("status") # => falseWhat it buys you in Ruby: fast comparison, free hashing, and a clean syntax for names that aren’t strings.<br>config = { host: "localhost", port: 5432, ssl: true }<br>config[:host]Hash keys are the obvious case, but the deeper use is method names. method_name and :method_name are the same idea at two levels. send(:save) calls the save method. define_method(:fetch) {…} defines one. respond_to?(:to_s) asks if one exists. Symbols are how Ruby refers to methods reflectively, which is how the metaprogramming works.<br>The &:foo shortcut from the last section is the same idea on a closer pass: a symbol naming a method, coerced into a callable. Symbols carry the names; Ruby looks them up.<br>Collection methods<br>map, select, reject, reduce, each, flat_map, zip, partition, chunk_while. The Enumerable module is the part of Ruby I would...

ruby lisp from method symbol symbols

Related Articles