Actually, OO code is far less reusable, and that's by design. The idea behind OOP is to restrict operations on particular pieces of data to certain privileged code that's either in the class or in the appropriate place in the inheritance hierarchy. This limits the adverse effects of mutability. If a data structure changes, there are only so many places in the code that can be responsible.
With immutability, you don't care who can operate on any given data structure, because no one can change your copy of the data. This makes creating new functions to work on existing data structures much easier. You just create the functions and group them into modules that seem appropriate from a domain point of view. You don't have to worry about where to fit them into the inheritance hierarchy.
The other kind of code reuse is creating new data structures to work on existing functions. This is handled in functional languages using features like generics and type classes. For example, Haskell's Ord type class allows you to use the sort
function on any type with an Ord
instance. Instances are easy to create if they don't already exist.
Take your Animal
example, and consider implementing a feeding feature. The straightforward OOP implementation is to maintain a collection of Animal
objects, and loop through all of them, calling the feed
method on each of them.
However, things get tricky when you get down to details. An Animal
object naturally knows what kind of food it eats, and how much it needs in order to feel full. It does not naturally know where the food is kept and how much is available, so a FoodStore
object has just become a dependency of every Animal
, either as a field of the Animal
object, or passed in as a parameter of the feed
method. Alternately, to keep the Animal
class more cohesive, you might move feed(animal)
to the FoodStore
object, or you might create an abomination of a class called an AnimalFeeder
or some such.
In FP, there is no inclination for the fields of an Animal
to always stay grouped together, which has some interesting implications for reusability. Say you have a list of Animal
records, with fields like name
, species
, location
, food type
, food amount
, etc. You also have a list of FoodStore
records with fields like location
, food type
, and food amount
.
The first step in feeding might be to map each of those lists of records to lists of (food amount, food type)
pairs, with negative numbers for the animals' amounts. You can then create functions to do all sorts of things with these pairs, like sum the amounts of each type of food. These functions don't belong perfectly to either an Animal
or a FoodStore
module, but are highly reusable by both.
You end up with a bunch of functions that do useful stuff with [(Num A, Eq B)]
that are reusable and modular, but you have trouble figuring out where to put them or what to call them as a group. The effect is that FP modules are more difficult to classify, but the classification is much less important.