Hidden Partial Application in Ruby

I really like functional programming patterns. React, Redux, and mpj inspired my JavaScript style, which has resulted in an overall fantastic experience writing JS applications. I don’t really relate to all the headaches that people say come with JavaScript because I started with the discipline of an FP perspective (to be fair, I also started JS when ES6 was in full swing so FP style was lovely to write).

With all the fun and benefits of FP I’ve come to know in JS, it’s hard not to import some of those patterns into Ruby. In the same way people might pass time doing sudoku or crossword puzzles, I like working through exercism.io challenges in a variety of languages, so I’m always faced with new opportunities to write fresh Ruby with a functional twist.

Recently I completed a Ruby implementation of the Luhn algorithm. Right away I saw it as a pure function: take some text and return a boolean (String -> Bool). In more spartan languages like JS (with ES6) and pure functional languages, this could be done with very few lines and characters, but the opinionated Rubyist in me and the structure of a PORO (plain ol’ Ruby object) require a bit more boilerplate and ceremony.

My goal is to write Ruby in a functional way that that still respects and celebrates the idioms of my beloved first language. Here was my first pass at the Luhn algorithm challenge:

Don’t worry too much about the implementation. Just know that it interrogates a string input and returns true if the string is valid and false if not.

I was happy enough with the solution, but I didn’t love how repetitive def method_name(raw) was. What choice did I have though if I want to write pure functions that only take inputs and return outputs?

My high-level goal is to check the validity of an input string based on different sets of criteria: length, character types, and a calculated sum based on the Luhn specification. In each method that takes raw as an input, I’m only reading it to calculate some new value. What if there was some built-in machinery in Ruby to let me easily read values without passing them into methods?

It’s like coming home

Right! Every Rubyist’s friend attr_reader ! On my second iteration I used an attr_reader value instead of passing raw to everything and removed the singleton class in favor of one of my favorite patterns in Ruby.

All my private helpers reach out of their scope for sanitized , so it seems pretty non-functional at first. But when I really look at those functions, they are private methods that will always return the same value given the same value of sanitized. In this way, it’s as if sanitized has just been partially applied to all these helper methods, and they just maintain access to a closure scope. I can be 100% confident in their behavior as long as sanitized stays read-only.

While it’s a lot more code than I might need in a JS implementation, I think it reads really well. It is very declarative up top, making you only dive deeper if you really want to. I originally used the Enumerable#all? pattern because I hated looking at

valid_length?(raw) && only_digits?(raw) && valid_sum?(raw)

It’s not too crazy, sure, but having to repeat that argument so many times made me just feel weird. I think this second pass is a nice blend of classic Rubyisms and functional flare. Let’s review the two:

Rubyisms and PORO Elements

  • Class method to instance def self.method; new.method; end
  • Every Rubyist’s favorite i word: initialize
  • attr_reader
  • private methods
  • ? suffixes for predicate methods

Functional Elements

  • Composition/chaining in Luhn#luhn_sum
  • Lambdas -> combined with Enumerable methods map(&luhn_double)
  • Variable names of x 🙀. I’m cool with this because I am obviously doing math so the vars are obviously math-able values (aka numbers). In others words, I don’t think naming it value , number , or even int adds much more meaning/intent than x .
  • Declarative by default. The assumption is that future developers only need to know what Luhn#valid? does, not how it works. If the developer wants to understand the implementation, they are free to do so, but not required. It would be totally adequate to just look at the definition of Luhn#valid? to get a good enough understanding of what is going on.

Conclusion

How long will my quest for the sweet spot of functional programming and Ruby (fRuby?) go on? Who knows. But using attr_reader as a roundabout form of partial application is a pleasant example of sticking to Ruby conventions and adding a very disciplined intent informed by concepts such as immutability and purity.

Ruby is fun to write. Functional code tends to run very well and is easy to reason about (given an understanding of the core principles). Reasonable an well-running code is also fun, so I think there is plenty of room to keep looking for ways to add functional flare to the Ruby community.