25

I come from an object oriented background where I've learned that classes are or at least can be used to make a layer of abstraction that allows for easy recycling of code which can then either be used to make objects or be used in inheritance.

Like for example I can have an animal class and then from that inherit cats and dogs and such that all inherit many of the same traits, and from those sub-classes I can then make objects that can specify a breed of animal or even the name of it.
Or I can use classes to specify multiple instances of the same code that handles or contains slightly different things; like nodes in a search-tree or multiple different database connections and what not.

I'm recently moving into functional programming, so I was starting to wonder:
How do purely functional languages handle things like that? That is, languages without any concept of classes and objects.

10
  • 2
    Why do you think that functional doesn't mean classes? The some of the frist classes came from LISP - CLOS Look at clojure namespaces and types or modules and haskell.
    – user40980
    Jun 9, 2013 at 18:58
  • 1
    Caml as an example, its sister language OCaml adds objects, but Caml itself doesn't have them. Jun 9, 2013 at 19:19
  • 12
    The term "purely functional" refers to functional languages that maintain referential transparency and is unrelated to whether or not the language has any object oriented features.
    – sepp2k
    Jun 9, 2013 at 22:42
  • 4
    The cake is a lie, code reuse in OO is far more difficult than in FP. For all that OO has claimed code reuse over the years, I have seen it follow through a minimum of times. (feel free to just say I must be doing it wrong, I'm comfortable with how well I write OO code having had to design and maintain OO systems for years, I know the quality of my own results) Jun 25, 2013 at 17:41
  • 2
    From an OOP background, FP is one of those things you simply cannot conceptualize without tasting the koolaid, so go learn and write some Haskell or Erlang (don't bother starting with Clojure or F#, you'll just write them imperatively and come away with the idea that FP is hogwash without ever using the paradigm). You'll learn what they're about then, many come away disliking them afterwards but at least they're informed how they work and solve problems. Jun 25, 2013 at 17:49

6 Answers 6

19

Many functional languages have a module system. (Many object-oriented languages do too, by the way.) But even in the absence of one, you can use functions as modules.

JavaScript is a good example. In JavaScript, functions are used both to implement modules and even object-oriented encapsulation. In Scheme, which was the major inspiration for JavaScript, there are only functions. Functions are used to implement almost everything: objects, modules (called units in Racket), even data structures.

OTOH, Haskell and the ML family have an explicit module system.

Object Orientation is about data abstraction. That's it. Modularity, Inheritance, Polymorphism, even mutable state are orthogonal concerns.

4
  • 8
    Can you explain how those things work in a bit more detail in relation to oop? Rather than simply stating that the concepts exist... Jun 9, 2013 at 19:14
  • Sidenote - Modules and units are two different constructs in Racket - modules are comparable to namespaces, and units are sort of halfway between namespaces and OO interfaces. The docs go into much more detail on the differences
    – Jack
    Dec 28, 2014 at 9:14
  • @Jack: I didn't know that Racket also has a concept called module. I think it is unfortunate that Racket has a concept called module that is not a module, and a concept that is a module but not called module. Anyway, you wrote: "units are sort of halfway between namespaces and OO interfaces". Well, isn't that sort of the definition of what a module is? Dec 28, 2014 at 12:21
  • Modules and units are both groups of names bound to values. Modules can have dependencies on other specific sets of bindings, while units can have dependencies on some general set of bindings that any other code that uses the unit has to provide. Units are parameterized over bindings, modules are not. A module dependent on a binding map and a unit dependent on a binding map are different in that the module must refer to some specific map binding, such as the one from racket/base, while different users of the unit can give different definitions of map to the unit.
    – Jack
    Dec 29, 2014 at 0:55
7

Although "functional programming" does not convey far reaching implications for issues of modularity, particular languages address programming-in-the-large in different ways. Code reuse and abstraction interact in that the less you expose the harder it is to reuse the code. Putting abstraction aside, I'll address two issues of reusability.

Statically typed OOP languages traditionally used nominal subtyping, which means that a code designed for class/module/interface A can only deal with class/module/interface B when B explicitly mentions A. Languages in functional programming family mainly use structural subtyping, which means that code designed for A can handle B whenever B has all the methods and/or fields of A. B could have been created by a different team before there was need for a more general class/interface A. For example in OCaml, structural subtyping applies to the module system, the OOP-like object system, and its quite unique polymorphic variant types.

The most prominent difference between OOP and FP wrt. modularity is that the default "unit" in OOP bundles together as an object various operations on the same case of values, while the default "unit" in FP bundles together as a function the same operation for various cases of values. In FP it is still very easy to bundle operations together, for example as modules. (BTW, neither Haskell nor F# have a full-fledged ML-family module system.) The Expression Problem is the task of incrementally adding both new operations working on all values (e.g. attaching a new method to existing objects), and new cases of values which all operations should support (e.g. adding a new class with the same interface). As discussed in the first Ralf Laemmel lecture below (which has extensive examples in C#), adding new operations is problematic in OOP languages.

The combination of OOP and FP in Scala might make it one of the most powerful languages wrt. modularity. But OCaml is still my favorite language and in my personal, subjective opinion it doesn't fall short of Scala. The two Ralf Laemmel lectures below discuss the solution to the expression problem in Haskell. I think this solution, although perfectly working, makes it difficult to use the resulting data with parametric polymorphism. Solving the expression problem with polymorphic variants in OCaml, explained in Jaques Garrigue article linked below, does not have this shortcoming. I also link to textbook chapters that compare the uses of non-OOP and OOP modularity in OCaml.

Below are Haskell- and OCaml-specific links expanding on the Expression Problem:

2
  • 2
    would you mind explaining more on what these resources do and why do you recommend these as answering the question asked? "Link-only answers" are not quite welcome at Stack Exchange
    – gnat
    Jun 10, 2013 at 14:39
  • 2
    I have just provided an actual answer rather than just links, as an edit.
    – lukstafi
    Jun 10, 2013 at 19:28
4

It looks like you're asking two questions: "How can you achieve modularity in functional languages?" which has been dealt with in other answers and "how can you create abstractions in functional languages?" which I'll answer.

In OO languages, you tend to concentrate on the noun, "an animal", "the mailserver", "his garden fork", etc. Functional languages, by contrast, emphasise the verb, "to walk", "to fetch mail", "to prod", etc.

It's no surprise, then, that abstractions in functional languages tend be over verbs or operations rather than over things. One example that I always reach for when I'm trying to explain this is parsing. In functional languages, a good way to write parsers is by specifying a grammar and then interpreting it. The interpreter creates an abstraction over the process of parsing.

Another concrete example of this is a project I was working on not long ago. I was writing a database in Haskell. I had one 'embedded language' for specifying operations at the lowest level; for example, it allowed me to write and read things from the storage medium. I had another, separate, 'embedded language' for specifying operations at the highest level. Then I had, what is essentially an interpreter, for converting operations from the higher level to the lower level.

This is a remarkably general form of abstraction, but it is not the only one available in functional languages.

1

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.

0

Modularity is designing so that things can be taken apart and put back together, sometimes substituting new parts for old ones.

OOP defines stateful objects and so modularity can include aggregation (building one object from several) and/or coordination (allowing collaboration via message passing). This will necessitate some form of wiring up (e.g. constructor dependency injection).

FP focuses on calculations. The stateful objects must still exist somewhere or nothing would get done. However, the modularity is managed in terms of how calculations (pure functions) are defined and assembled.

Eric Normand adroitly describes programs as being made up of actions, calculations and data. Thus modularity involves the decomposing and composing of data and actions (in impure OOP Land) or in data and calculations (in pure FP Land).

-1

One of popular solutions, is to break code into modules, here is how it's done in JavaScript:

    media.podcast = (function(name) {
    var fileExtension = 'mp3';        

     function determineFileExtension() {
         console.log('File extension is of type ' + fileExtension);
     }

     return {
         download: function(episode) {
            console.log('Downloading ' + episode + ' of ' + name);
            determineFileExtension();
        }
    }    
}('Astronomy podcast'));

The full article explaining this pattern in JavaScript, apart from that there is number of other ways to define a module, such as RequireJS, CommonJS, Google Closure. Another example is Erlang, where you have both modules and behaviours that enforce API and pattern, playing similar role as Interfaces in OOP.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.