...but Something is too much

A new year, a new blog! What better way to start than with an appeal for minimalism in code (and perhaps life)?

A few days ago, I watched Sandi Metz’ great talk “Nothing is Something” based on a recommendation by Monica Lent.

In her talk, Sandi argues in favor of composition and to be wary of inheritance. She gives a typical example of what happens when inheritance is used to share behavior:

class RandomHouse < House
  def data
    @data ||= super.shuffle
  end
end

class EchoHouse < House
  def parts (number)
    super.zip(super).flatten
  end
end

# don't do this
class RandomEchoHouse < House
  def data
    @data ||= super.shuffle
  end

  def parts (number)
    super.zip(super).flatten
  end
end

Obviously, this leads to brittle code with lots of duplication. As a solution, Sandi advocates a combination of dependency injection and composition using multiple behavioral classes:

DATA = ['1', '2', '3', '4', '5', '6']

class DefaultFormatter
  def format(parts)
    parts
  end
end

class EchoFormatter
  def format(parts)
    parts.zip(parts).flatten
  end
end

class DefaultOrder
  def order(data)
    data
  end
end

class RandomOrder
  def order(data)
    data.shuffle
  end
end

class House
  attr_reader :formatter, :data
  def initialize(DefaultOrder.new, DefaultFormatter.new) # DI
    @formatter = formatter
    @data = orderer.order(DATA)
  end

  def parts(number)
    formatter.format(data.last(number))
  end
end

class RandomHouse
  [...]
end

class RandomEchoHouse
  [...]
end

Indeed, this makes the code simpler and more maintainable than before.

Still, I couldn’t help but think: Why not do away with this whole class structure? Why do we even have to create multiple classes for something that is a simple functional data transformation?

Here is my Clojure implementation for the same task:

(def data ["a" "b" "c" "d" "e"])

(defn echo [data]
  (interleave data data))

(defn shuffle-it [data]     ;; could be inlined
  (shuffle data))

(defn echo-shuffle [data]
  (echo (shuffle-it data)))

This is still more verbose than necessary in the interest of comparability.

One might argue that the class structure is useful for code organization in a real world use case; for that, we simply could have used namespaces.

This data-first approach isn’t limited to a purely functional language like Clojure. The same thing can be achieved with a few lines of Rust:

use std::fmt::Display;

fn main() {
  let mut data = vec!["a", "b", "c", "d", "e"];

  // "RandomEchoHouse"
  shuffle(&mut data);
  echo(&data);
}

fn shuffle<T>(data: &mut Vec<T>) {
  [...]
}

fn echo<T: Display>(data: &Vec<T>) {
  print!("[");
  for v in data {
    print!("{}, {}, ", v, v);
  }
  println!("]");
}

Perhaps I’m simply missing something here. Perhaps that’s a good thing.