Zero Ceremony Assignment
One of Ruby's most fundamental delights is its ability to perform variable assignment
without any ceremony. In many programming languages, variable declaration requires
explicit keywords or types, adding tedious verbosity. Ruby, on the other hand,
allows you to assign values to variables directly and intuitively.
variable = "I'm a String" variable = "You can change me and that's fine!"
// Without a declaration keyword, this is assigned to the window/global object
variable = "I'm a globally scoped String";
// Declaring with 'var' like in the olden days of glory
var variable = "I'm a String";
// But these days idiomatic JS uses 'let'
let variable = "I'm a String";
variable = "You can change me and that's fine!";
// Or 'const' for "constants" (🙈)
const constant = "I'm another String!";
constant = "This will throw an error!";
I've personally never encountered a bug which let or const would have prevented. Hallelujah to Ruby's simplicity! This is the sort of simplicity that allows translating an idea in the programmer's head into code with full signal and zero noise.
Have I encountered bugs which stricter typing would have prevented? Yes. But the flow and brevity provided by Ruby's style make the trade-off a no-brainer for my personal taste. Let's contrast with C, one of the OGs of statically typed languages.
string = "I'm a String"
small_integer = 42
large_integer = 12345678901234567890
float = 3.14
array_of_numbers = [1, 2, 3]
array_of_whatever = [1, "two", 3.0, :four]
hash = { name: "Alice", age: 30 }
nothing = nil
boolean = true
#include <stdbool.h>
#include <stddef.h>
struct Person {
const char *name;
int age;
};
int main(void) {
const char *string = "I'm a String";
int smallInteger = 42;
long long largeInteger = 1234567890123456789LL;
double floatNumber = 3.14;
int arrayOfNumbers[] = {1, 2, 3};
const char *arrayOfStrings[] = {"one", "two", "three"};
struct Person person = {"Alice", 30};
void *nothing = NULL;
bool myBoolean = false;
return 0;
}
In C, every variable declaration requires an explicit type and additional code for structured data. This verbosity hinders the rapid translation of ideas into code, as the programmer's attention gets boggled down with satisfying ceremonial requirements of the language instead of just "doing the thing".
Sacrificing static type checking is by far the most controversial aspect of dynamically typed languages like Ruby.
Notably a huge portion of the JavaScript community has decided to abandon the actual language JavaScript in favour of TypeScript, mostly due to a preference for stronger typing.
The Ruby community has also experimented with approaches to enhance type-checking. Some projects include:
- Sorbet - A gradual type checker for Ruby created by Stripe.
- RBS - Ruby's own type signature language.
- Empirical - Enhancing Ruby with syntax for runtime type assertion.
- T-Ruby - Ruby with syntax for types.
However, none of these have taken off in a big way. They act as an opt-in for those who have a strong preference toward static type checking, and they can probably make a lot of sense in large organisations with humongous code bases spanning millions of lines of code like Stripe's and Shopify's.
There is plenty of evidence that successful products and maintainable code bases at the scale of hundreds of thousands of lines of code can be achieved with Ruby as is. I would personally advise programmers getting into Ruby to consider how to make the most of the benefits of dynamic typing rather than fight against it. For those who want to know what a fully statically typed Ruby would feel like I suggest taking a look at the Crystal programming language.