1. 29
    1. 10

      This is one of the things I love about Ruby. Most of the syntax is just sugar for a method call (or, technically, sending a message), and with the ability to monkeypatch everything you can rewrite the language to suit your fancy within Ruby itself!

      What’s cool about it is that it’s not like a Lisp or Rust or C or other languages that let you define macros which require a separate pass in the compiler to expand them (although in Lisps the line between macro and language construct is very blurred by virtue of the syntax) or like a Forth where you have to define parsers for new words; Ruby has a well-defined syntax and no macro system, so it’s actually more similar to defining __enter__ and __exit__ in Python to make a class compatible with the with syntax. It’s just that Ruby has syntax sugar for method calls where other languages would have custom language constructs, so you can often fall back to the “core language” to redefine what a particular bit of syntax desugars to.

      Languages often have these bits of syntax or other features that are linked to a restricted set of types and that you can’t instrument other types to use them with (like ? in Rust, which currently only works on Result and Option instead of definining a Try trait that you can implement on other types, or Elm’s special type classes like “number” that are baked into the compiler, which probably take after SML’s arithmetic functions that work on both int and real, etc). I like to call these things “compiler magic”, and I think it’s nice that Ruby has very little compiler magic.

      1. 2

        I don’t think that’s quite right. That sounds similar to Common Lisp macros, but less powerful.

        A macro in Common Lisp is a function that runs at compile (or interpret) time and transforms one block of code into another block of code before actually compiling or interpretting it.

        It boils down to the same thing in a purely interpretted language like Ruby because “runtime” and “compile time” (or “interpretation time” in Ruby, I guess), but makes a major difference when compiling.

        1. 2

          This meta programming style is mostly inherited from Smalltalk!

        2. 6

          With that title, I can’t pass up the chance to link to my own criminally bad Ruby code, which was an entry for a contest called BadCode.rocks. Unlike this post, most of that code’s badness comes from its convoluted algorithm for simulating a robot’s movements, but like this post, the code uses unnecessary abstractions, misleading names, and unidiomatic Ruby code. All of the code’s problems are explained in the README.

          Some examples of misuse of Ruby-specific features: my entry’s code uses ARGV.zip(processors).map with an array of callables to parse command-line arguments. It uses this_run.end(0) to mean the length of the this_run array. And it uses both mov[-1] and mov[1], though in context they always refer to the same element.

          1. 1

            This is fantastic. Thanks for sharing!

          2. 5

            Delightful. On its way to Raku or APL levels of crime.