1727 words
9 minutes
Joy! A love letter to Ruby and scalar objects
2026-04-28

A very rambling introduction#

I’m part of the slowly dying breed of people who got into computers as childhood escapism. This originally took the form of playing about with Microsoft Access - playing about with the forms in VBA and feeling like I was doing something by copying scripts out of books from the library.

As time passed, our school computers had a copy of Visual Basic 6.0 installed - and I loved it. You could create programs through a drag and drop interface! You could make anything (as long as you could find a library for it)! In many ways I think the way we develop software has all been downhill from here, but I digress!

Unfortunately, while this allowed me to spend time messing around with programs at school, it didn’t help me with what I could play about with at home.

That’s when PHP walked into my life. (Praise be to the maintainers of Xampp back then I guess) It would stay there pretty steadily for… 15 years maybe? It’s a fantastically maligned piece of software - as the phrase goes, there are two types of programming languages, that which people complain about and those that people don’t use.

While the classic piece of blogging on the topic is PHP a fractal of bad design (some of which is fair and a lot which is no longer relevant) - the big thing that gets brought up time and time again is how the methods are named. It has perl and C roots and we get a massive mess of functions where the naming is inconsistent and the parameter order is incredibly unintuitive.

This never really bothered me on such a viceral level - after all, by the time we got to 2012/2013 we had PHPStorm saving us from most of this; and it was still much nicer than the fights I had back when we were all writing frontend JS without a framework (anyone remember Array.prototype.slice.call(obj)?)

Still, I had to acknowledge that one thing that was nicer in JS is that when an object had proper scalar support, the interface was much more intuitive. ["foo","bar"].slice(1) is much nicer than array_slice($array, 1) after all and the tab complete-ness of the IDE support was always nice.

There have been a lot of RFCs put forward by well meaning developers who thought they could fix this, that they could make the naming good and then this problem would be solved. The problem of course always being that unless we had a plan to deprecate the old ones, we’ve now just replaced the problem with having two identical methods - a lot of mess, not much in the way of actual language improvement.

At this time in PHP, we were blessed with Nikic at the helm - a developer who did an incredible job of improving a huge amount in the ecosystem. While he eventually went onto bigger and better things one thing he did raise was the idea of scalar objects.

This, to me at the time, was incredible. It was something which would be meaningfully different and give us a solid path forward with getting something better. We could have methods off strings like $string->length() - it could be beautiful!

Unfortunately, this was really a proof of concept implementation - nobody ever ran with it, nobody ever took it further.

What’s this got to do with Ruby?#

I eventually left the job where I was writing a lot of PHP and moved to a company that was using Ruby. Specifically, the codebase I was using was using Ruby on Rails.

This was pretty eye opening really - the combination of an excellent standard library and the block syntax leads to code that feels so nice to write.

At this point I’d been writing code in a functional style for a long time (.filter, .map, .reduce) but the level of expression you could get if you could use Rubys STD is fantastic - everything can feel so clean!

An exert from an advent of code
value = STDIN.read.lines(chomp: true)
.map{|line| line.split("")}
.in_groups_of(3)
.map{|elf1, elf2, elf3| elf1 & elf2 & elf3 }
.flatten
.map{|item| item.ord > 96 ? item.ord % 96 : (item.ord % 64) + 26}
.sum

Of course, in Ruby, there is a horrible downside to all of this - it has terrible support for typing. While at this point modern PHP was all in on types for everything but generics and TS had JS in a firm chokehold, the regression to the meagre type support you could implement in Ruby was… not great.

The company at least was using Sorbet - a type system with a pretty horrible syntax that lived in user land for Ruby

ewww
sig { params(name: String).returns(Integer) }
def main(name)
puts "Hello, #{name}!"
name.length
end

For a language that prizes itself on how syntactically clean it feels to write, this always felt pretty nasty. it also firmly gets in the way of beautiful feeling functional code (at least, to me)

I’m getting to the point I promise#

As big tech is big tech, I “moved on” from this job and Ruby became something that fell out of my day to day.

Once a year I picked it up to play around with Advent of Code - the place where I can write functional code that isn’t a million miles away from code golf without anyone screaming at me.

The problem that I kept on finding year after year however is that… well, the typing system still sucks. The standard library is fantastic to use, but time after time the syntax would trip me up and I’d spend an annoying amount of time trying to work out what went wrong. Call me Icarus I suppose.

I’m at the point! We’re there! Congratulations!#

When it came around to last year, I got to thinking - why is it that I only get to play with this kind of program writing if I’m using Ruby? Hello Array.protoype...

For those who weren’t around for the first time - the JS community has a very distrusting relationship with using adding your own custom methods to built in objects. Back in the day there was popular library named prototype.js that had the bright idea of doing so for a bunch of things.

Unfortunately, they reached a point of popularity where when JS wanted to introduce new methods, they found themselves staring down the gun of a very unfair BC break - if you’re writing a JS engine for a browser and adding your new method will break {x} thousand websites - then you’re going to have to think of a different name.

It also had some really annoying implementation side effects - if you defined things using Array.prototype, they were enumerable, which meant that if you then iterated through the object, it’d be counted.

bad idea oh no
Array.prototype.first = function() {
return this[0];
};
const myList = ['cat', 'hat'];
for (let index in myList) {
console.log(index); // would go "cat", "hat", "first" - arghhh
}

Come 2026 though, the equation is a little different. We commonly run JS on the backend where we’re at the mercy of the JS engines that we choose to run, not whatever choices have been made by the (increasingly small) number of differing browser implementations.

We can also avoid the issues of enumeration now as there’s a more powerful way to define properties

progress!
Object.defineProperty(Array.prototype, 'first', {
get: function() {
return this[0];
},
enumerable: false,
configurable: true
});

Finally, if there are collisions, the tooling is so much better these days it’s a relative breeze to rename whatever needs to be renamed.

With all that in mind, I’d like to introduce Joy - my abomination library that should under no circumstances be used on the web. Or in production.

What does it do?#

It gives us a more feature complete JS standard library, that’s what it does!

Most importantly it gives me the fun I found in Ruby, but with greatly reduced footguns; all of the functional programming beauty, but with all of the power of typescript saving me from the consequences.

If you screw something up chances are you’ll get some beautiful squiggly lines and can continue with your day.

Okay, but what batteries are you getting?#

There are a bunch of methods that I miss from Ruby in JS, but the most common 2 are .eachWithObject and .then/.yield_all.

Let’s say I had a problem where I’d been given a large number of strings and I wanted to make them all unique.

If we’re wanting to write this in a functional style, we’d want to be making use of reduce.

strings.reduce((acc, item) => {
acc.add(item);
return acc;
}, new Set([]))

The first issue here that makes this ugly is I’m having to return acc each time - in this instance I know I’m working on acc, can I make this cleaner?

Yes I can!
strings.eachWithObject(new Set(), (acc, item) => acc.add(item));

With eachWithObject we’re never overridding the accumulator!

Now, this is all great but… I didn’t want a set - I wanted an array of all these items? I was going to upper case them! We’d normally have to fallback to a more conventional JS style:

Argh! Not functional!
[...strings.eachWithObject(new Set(), (acc, item) => acc.add(item))]
.map(v => v.toUpperCase())

This isn’t great though, as we’re no longer reading in order - I’ve lost some beauty and readability! This is where (in Ruby) .then/.yield_all comes in handy! In Joy, we’re calling it .chain as adding additional meaning to .then would be a horrific thing to do.

A bit more functional?
strings
.chain((arr) => [...arr.eachWithObject(new Set(), (acc, item) => acc.add(item)])
.map(v => v.toUpperCase())

You might be wondering if that’s really much of an improvement - and in this tiny example? Possibly not. If you’re doing something more elaborate, it most certainly is.

just an example of the kind of abomination you can write!
#!/usr/bin/env bun
import { readStdin, println } from "../helpers";
const value = (await readStdin())
.trim()
.split("\n")
.map((v) => v.split(""))
.transpose()
.eachWithObject([] as { op: string; values: number[] }[], (acc, col) => {
const [op, num] = [col.pop()!, Number(col.join(""))];
if ("*+".includes(op)) acc.push({ op, values: [] });
if (num > 0) acc.last()!.values.push(num);
})
.sum(({ op, values }) => (op === "+" ? values.sum() : values.product()));
println(value);

Should I use this?#

Are you writing local scripts for yourself? Are you writing some code golf? Are you wanting to have fun? Then yes! Do it!

Are you wanting to use this in production? Don’t do it - it’s not worth it! Even if you hold off on updating your node version, libraries could hypothetically look for new methods on objects and get bitten.

I mean, realistically I think you’d be fine, but boy would I be mad if I found someone had included this in one of my codebases. Definitely don’t include it in a library

Joy! A love letter to Ruby and scalar objects
https://dochne.com/posts/joy/
Author
Doug Nelson
Published at
2026-04-28
License
CC BY-NC-SA 4.0