Functional-ish Ruby, Part 2: Functional Objects

In a previous blog post I discussed a few ways to think about writing Ruby methods from a functional perspective. But in Ruby, or any other object oriented language, we don't just write functions, we write objects. Does that mean we either have to live without the principles we like from functional programming, or fundamentally change the way we write Ruby or OO code? I'd argue, neither. Let's not conflate paradigms that we have grown to like or dislike in OO and functional programming with objects themselves, or the lack thereof.

What are the benefits to programming functionally? There are many, and this blog isn't about defining them, or validating what specific concepts actually define functional programming. This blog assumes that there are certain ideas attached to the grander idea of "functional programming," and that you see some value in them.

So, given that things like referential transparency, pure functions, immutable state, currying, closures, higher order functions, and monads are things that we like, what does that mean for those who predominately work in object oriented languages? Well, we can still write Ruby, or any other object oriented language perfectly fine while adhering to our favorite functional principles. Here are some examples:

Immutable State and Avoiding Side Effects

Take the following object:

class MutableObject
  attr_accessor :mutable_attrs

  def initialize(attrs = {})
    @mutable_attrs = attrs

  def update(key, value)
    mutable_attrs[key] = value

object =
object.update(:some_key, :new_value)
# or
object.mutable_attrs[:some_key] = :new_value

In this class, not only does the update method change the state of the object, but we've exposed our attrs hash through a public attr_accessor, leaving it easily exposed for mutation in place. What's the problem? Well, maybe nothing if the scope of this object is localized enough so that everything using the object knows that it could change. But once the mutations leave a defined boundary, we've introduced more complexity to the codebase.

We don't need truly immutable data structures to fix this problem, we just need to be mindful of when we actually should mutate state. If we decide that mutating state causes unnecessary complexity, we could refactor this class to look more like this:

class MyObject
  def initialize(attrs = {})
    @attrs = attrs

  def update(key, value), value))


  attr_reader :attrs

object =
new_object = object.update(:some_key, :new_value)

Now when we update an object we return a new instance. That way we know that once we have an object, it won't change.

The nice thing about a functional language like Clojure is that we get this for free.

(def my-map {:number 1})

(def my-new-map (assoc my-map :number 2))

(:number my-new-map) ; => 2
(:number my-map) ; => 1

But, as explained before, sometimes mutation is ok, even better, if it's localized. And even Clojure admits this by allowing a data structure to explicity be declared as transient:

(defn vec-range [n]
  (loop [i 0 v (transient [])]
    (if (< i n)
      (recur (inc i) (conj! v i))
      (persistent! v))))

State and mutability themselves aren't the problem. The problem is mutating across boundaries. And we can address that by being aware of when we are mutating state, or hiding our objects' state behind method calls rather than exposing public accessors.

Referential Transparency in Enumerables

One pattern to be aware of with Ruby's enumberables is that the each method on its own only returns an enumerable for the original collection. So, it can only have an effect by changing state somewhere else and cannot be referentially transparent.

class ThingFilter
  def initialize(things)
    @things = things

  def call
    @things.each do |thing|


Consider using map or reduce, and if you can't, it's a sign that the operation is mutating state, and is not referentially transparent or a pure function.

def call do |thing|
    slightly_transform(thing) do |thing|
def call
  @things.reduce({}) do |name_hash, thing|
    name_hash[] =

Currying, Closure, and Function Instantiation

The important thing to remember about objects is that they are just functions that have been instantiated with local bindings. is just a function that returns a new function to be called later with a local binding to 5. To start, consider a class method that takes three arguments, and does something immediately with the three arguments. It's clearly just a function attached to a class namespace.

class Foo
  def self.a_function(a, b, c) + b + c)

Foo.a_function(10, 11, 12)

But, what if we just want to bind two arguments and return a new function to be called later? Well, then we instantiate a new object with two of our arguments, which returns a new function to be called with one argument later on.

class Foo
  def initialize(a, b)
    @a = a
    @b = b

  def a_function(c) + @b + c)

foo_fn =, 11)

In functional languages we do this through just returning a function, from a function. Or, in Clojure, if we want to just return a function that takes one argument, we can use the partial function.

(defn foo [a b]
  (fn [c]
    (do-the-thing (+ a b c))))

((foo 1 2) 3)

(defn foo [a b c]
  (do-the-thing (+ a b c)))

((partial foo 1 2) 3)

To underscore the fact that functions are just objects with one method, Clojure's functions are implemented in Java as an interface with one function, invoke.

Higher Order Functions

If objects are just functions, we can pass them around and return new functions.


class OperationManager
  def initialize(thing_doer)
    @thing_doer = thing_doer

  def get_presenter(thing)
    result =

operation =
presenter = operation.get_presenter(my_thing)

Here, we can look at the class OperationManager as a function that takes a thing_doer function, then returns a new function that takes a thing argument. When get_presenter is called with a thing, it returns a function that can be called with an output stream to finally show the result. In Erlang, this pattern might look like this:

manage(Doer) ->
    fun(Thing) ->
        Result = Doer(Thing),
        fun(Out) -> write_out(Out, Result) end

do_something(MyThing) ->
    ActionFn = manage(fun my_doer_fn/1),
    PresenterFn = ActionFn(MyThing),

What's the benefit of this over one function that does everything at once? One, we've made the object more composable by allowing how the thing gets done to be passed to it. Two, perhaps the three arguments — the thing_doer, the thing, and the output stream — are known at different places in our program, and are responsiblities of different objects. We've now hidden the details of the two other arguments that those objects don't care about.


Another fun application of higher order functions is monads. A monad is basically just a function that allows us to perform a connected series of operations on an object. Consider the common "maybe" monad, which connects a series of operations on a value that may or may not be present.

class Maybe
  attr_reader :value

  def initialize(value)
    @value = value

  def try
    if value.nil?
      result = yield value

maybe =
value = maybe.try { |x| x.to_s }.try { |x| nil }.try { |x| x + 1 }.value

The value here would be nil, but notice that there would not be an exception when trying to perform x + 1 when x is nil.

In languages like Haskell or Rust, we get something like this for free. In Clojure, a rudimentary maybe monad might look like this:

(defn maybe [x]
    ([op] (if (nil? x) (maybe x) (maybe (op x))))
    ([] x)))

(((((maybe :foo) str) (fn [x] nil)) (fn [x] (+ 1 x))))

Lately, I've been experimenting with a result monad, which is useful for eliminating nil checks or abstracting event statuses or reasons for failure. It's not much different than the maybe monad, the only difference is that we check for matching statuses, rather than nil.

class Result
  attr_reader :result

  def self.status(status, result)
    new(status, result)

  def initialize(status, result)
    @status = status
    @result = result

  def on_status(status)
    if @status == status
      value = yield @result
      Result.status(@status, value)
      Result.status(@status, @result)

result = Result.status(:not_found, nil)

title = result.on_status(:success) do |post|
end.on_status(:not_found) do
  "Title not found"


So, what does this mean? To me, it means that if we want to write functional code in an OO language we just need to ask ourselves, "Could I easily model this object as a function?" If the answer is no, then that's probably a sign that it's changing too much state, or is not composable, or is not meeting some definition of the word functional. The ideas that we like about functional programming can take many different forms, and these ideas don't have to be a prescriptive ideology. There are obviously situations where enforcing immutable state or using a monad makes our code more complex. But, if you see value in using immutable state, or higher order functions, or a monad, then use it. Take the pieces you like when they fit, and find the alternative best option when they don't.

Kevin Buchanan, Software Craftsman

Kevin Buchanan is biking around Chicago looking for donuts on his way to write code

Interested in 8th Light's services? Let's talk.

Learn more about our Ruby services

Contact Us