
Functional Thinking after OO - fogus
http://squirrel.pl/blog/2011/03/14/two-worlds-learning-functional-thinking-after-oo/
======
apinstein
Very interesting article. As a primarily OO programmer that's been playing
more and more with functional concepts due to javascript, I definitely see the
theoretical advantages mentioned in the article.

However, I also see that there are very hard parts to purely functional as
well. For instance, to be entirely side-effect free your language has to be
able to enforce immutability (which JS lacks). It's also hard (or at least not
obvious) how to do certain types of logic flows in a purely functional.

For small domain spaces I can visualize how purely functional bottom-up
programming effectively causes you to write a DSL for your problem.

However I don't have the experience/wisdom to understand if this holds true as
the size of the domain increases. Basically, I have concerns that purely
functional solutions don't scale (not from a performance perspective, but from
an organizational/architectural one).

My experience with complex business logic makes me worried that complex new
business rules would be difficult to implement in purely functional way and
would cause frequent, painful refactoring.

I would love to hear from someone that's done a purely functional application
with a large surface area that has been subjected to a real-world business
environment to comment on the subject.

~~~
francoisdevlin
Functional programming does scale very well conceptually, and you've been
using it for quite some time. It's just had a different name:

Unix Pipes

I believe using them to compose several small programs together to get a new
solution has been fairly well documented :)

~~~
silentbicycle
Unix pipes aren't functional programming (which emphasizes immutability and
referential transparency) - they're more like the actor model, which creates a
system out of lots of small programs communicating via message passing. And
yes, they do scale very well, conceptually.

Erlang does both, maybe that's where the confusion is coming from. They're
very complementary, though: having separate, concurrent processes seems to be
an excellent pressure release for difficulties that can build up doing pure
functional programming.

Also, Erlang has an novel (and extremely effective!) error-handling system for
the actors, while error handling for Unix pipelines (or asynch/event-loop
systems!) can be tricky.

~~~
thesz
>Unix pipes aren't functional programming (which emphasizes immutability and
referential transparency) - they're more like the actor model, which creates a
system out of lots of small programs communicating via message passing.

Unix pipes are lazy lists.

Operations on lazy lists allow you to use Mealy machine [1] created from pure
functions. Mealy machine uses pure functions that transforms input and
previous state to output and next state, which state then goes back on the
next cycle.

And then you suddently have Turing completeness (because of arbitrary state).

My argument is supported by the fact that Unix pipes could not change the
topology of computation structure, so do lazy lists, while actors could.

[1]: <http://en.wikipedia.org/wiki/Mealy_machine>

~~~
silentbicycle
That makes sense.

------
djacobs
My process is something like this:

1\. Start with code centered around pure functions. This is a really good
default for your project. It gives you flexibility, completely DRY code, and
an easy way to think about concurrency.

2\. Store large datasets or shallow objects inside maps. They can be efficient
and work really well with functions, especially in languages like Python and
Clojure. They're even more useful when, for example, they have a literal dot
notation (Javascript) for keys.

3\. Sometimes, though, it's just easier to structure data inside of objects.
Having a map of maps of maps is fine, but projects can quickly become
confusing--at least in my experience. The problem is especially bad if
multiple people are coding around maps without any enforced structure or
validation on creation. In these cases, objects can make things simple and
(often) more unified. When I use objects, though, it's good to avoid mutable
state. I think of an object as a handy, unified interface to a map, but with
the following extras in a unified way:

    
    
      - A constructor, potentially with sensible defaults
      - Validations and sensible fallbacks
      - Data-specific error messages
      - Methods that are closely related to the data it 
        holds (and always return the object itself, so 
        chaining is simple)
    

In my perspective, building code using functions and organizing it around
objects is a nice middle point. It's optimized for both productivity and
order, especially if you're careful to avoid state.

~~~
weavejester
> _The problem is especially bad if multiple people are coding around maps
> without any enforced structure or validation on creation._

Using a functional style doesn't mean you can't use constructors, and
constructors don't necessarily mean you have to use objects.

~~~
djacobs
Sure, you can have all of the things I listed in FP with some effort. My point
is just that OO seems to be good at bringing them all together in a really
useful way. (And that way isn't inconsistent with FP.) I aim to strike a
balance, to use each style where it is strongest.

~~~
weavejester
Ah, I see your point. But... whilst I can see that a lot of the functionality
objects provide is useful, there is also a lot of functionality one might not
necessarily need in all instances.

------
colomon
I'm not quite sure what to make of this article. On the one hand, I completely
agree that bottom-up programming is a great approach.

On the other hand, I completely disagree that top-down programming is strongly
tied to OO programming. At the very least, it certainly is possible to do
bottom up programming in OO languages. That's my normal working mode...

~~~
kirbman89
I agree. I'm guessing that the author doesn't even know what his database
tables look like. Starting top-down doesn't make sense. As an OO programmer,
it's not best practice to start a project using concrete classes. We should
always code with scalability and flexibility in mind.

~~~
tieTYT
Did you forget to put a sarcasm tag at the end of that?

------
joshwa
I'm curious about what I view as two contradictory points in the article: If I
create my own language/syntax layers as I go through the various layers of my
program, how does that make it more maintainable?

This obviously works for the single programmer, who understands the code he's
written (assuming he/she hasn't been away from the code too long), but what
happens when you have to maintain someone else's code? If I have to jump into
a particular spot on the code to solve a problem, how do I know you've defined
<+- as some special monad?

As a maintenance programmer, it seems like I'd have to learn a whole new
language with each program I have to maintain. (and I, the hypothetical
maintenance programmer, am probably not as smart as the original programmer).

~~~
noidi
From On Lisp by Paul Graham (pages 59-60):

If your code uses a lot of new utilities, some readers may complain that it is
hard to understand. People who are not yet very fluent in Lisp will only be
used to reading raw Lisp. In fact, they may not be used to the idea of an
extensible language at all. When they look at a program which depends heavily
on utilities, it may seem to them that the author has, out of pure
eccentricity, decided to write the program in some sort of private language.

All these new operators, it might be argued, make the program harder to read.
One has to understand them all before being able to read the program. To see
why this kind of statement is mistaken, consider the case described on page
41, in which we want to find the nearest bookshops. If you wrote the program
using find2, someone could complain that they had to understand the definition
of this new utility before they could read your program. Well, suppose you
hadn’t used find2. Then, instead of having to understand the definition of
find2, the reader would have had to understand the definition of find-books,
in which the function of find2 is mixed up with the specific task of finding
bookshops. It is no more difficult to understand find2 than find-books. And
here we have only used the new utility once. Utilities are meant to be used
repeatedly. In a real program, it might be a choice between having to
understand find2, and having to understand three or four specialized search
routines. Surely the former is easier.

So yes, reading a bottom-up program requires one to understand all the new
operators defined by the author. But this will nearly always be less work than
having to understand all the code that would have been required without them.

If people complain that using utilities makes your code hard to read, they
probably don’t realize what the code would look like if you hadn’t used them.
Bottom-up programming makes what would otherwise be a large program look like
a small, simple one. This can give the impression that the program doesn’t do
much, and should therefore be easy to read. When inexperienced readers look
closer and find that this isn’t so, they react with dismay.

We find the same phenomenon in other fields: a well-designed machine may have
fewer parts, and yet look more complicated, because it is packed into a
smaller space. Bottom-up programs are conceptually denser. It may take an
effort to read them, but not as much as it would take if they hadn’t been
written that way.

~~~
akkartik
I think bottom-up programming is great, but I have a bone to pick with that On
Lisp quote.

 _"So yes, reading a bottom-up program requires one to understand all the new
operators defined by the author. But this will nearly always be less work than
having to understand all the code that would have been required without
them."_

Here's the problem: it's hard to build good abstractions. Every abstraction
has a cost and an overhead in learning it, getting used to it, managing the
cognitive overhead. The binary black-and-white phrasing of this argument
utterly sidesteps any mention of the tradeoffs involved. Most abstractions we
encounter in real-world code fail to take into account the cost of using an
abstraction. You built it, you're used to it, you can't empathize with others
who need to learn it.

Here's my stab at articulating the tradeoffs:
<http://news.ycombinator.com/item?id=2329613>

Common Lisp is a great language, but it's _littered_ with crappy abstractions:
too many kinds of equality <http://www.nhplace.com/kent/PS/EQUAL.html>, an
inability to override or extend _coerce_ , redundant control abstractions like
keyword args to reverse traversal order
(<http://dreamsongs.com/Files/PatternsOfSoftware.pdf>, pages 28-30), the list
goes on and on.

~~~
loup-vaillant
There's even worse than abstractions that are used by their creator only:
those that are not used by their creators _at all_.

I see that in my code all the time: my abstractions tend to suck until I use
them myself, at which point I fix them.

The problem is, we often have to build abstractions that others will use
before we use them ourselves. At that point we're kinda stuck, because the
necessary changes will break code, and that's scary.

------
AndrewHampton
pg quote from the article: you don’t just write your program down toward the
language, you also build the language up toward your program. As you’re
writing a program you may think "I wish Lisp had such-and-such an operator."
So you go and write it. Afterward you realize that using the new operator
would simplify the design of another part of the program, and so on.

I remember thinking when I first read through pg's essays a few years back and
again now that you can do this in declarative languages as well using
functions.

For example, my programs often looks something like this:

    
    
      main(){
        var data = getData();
        pushData(data);
      }
    
      function getData(){
        var data = fetchDataFromSource();
        var errors = validateData(data);
        if(errors != null)
          //handle errors
        return processData(data);
      }
    
      function fetchDataFromSource(){
        ...
      }
    
      function validateData(var data){
        ...
      }
    
      ...
    

Could anyone explain why functional languages are better for this? It seems to
me this is the same as "building the language up to your program".

~~~
joevandyk
for example, getData() could be written something like:

    
    
      getData = fetchDataFromSource . validateData . ProcessData
    

See <http://www.haskell.org/haskellwiki/Function_composition>

~~~
lzm
How would you handle validation errors in this case?

~~~
lallysingh
Two ways: (1) (avoid) something similar to exceptions; (2) (much better) your
datatype has an 'invalid' value. See Maybe in haskell, or Option in Scala (I
think that last one's right...).

~~~
eru
With error handling in Haskell your code would look like

    
    
       getData = fetchDataFromSource =<< validateData =<< ProcessData
    

By the way, the original getData with (.) is backwards. It should be:

    
    
      getData = ProcessData . validateData . fetchDataFromSource

------
SeanDav
No doubt functional programming has its place and is very powerful for many
types of problems. Just curious, how do you maintain your lovely side-effect
free structure while programming a GUI or other heavily event-driven
environment?

I have a genuine interest in the answers to this as it is one of the things
putting me off learning an otherwise very interesting paradigm.

~~~
jerf
GUIs themselves aren't the problem; the problem is that all GUI toolkits are
written imperatively and since you don't want to rewrite your GUI from scratch
you have to end up interfacing your functional program to a huge hunk of
imperative code. But it is important to be clear that the problem is actually
just the impedance mismatch between the functional code and any huge hunk of
imperative code, not special to the problem of GUIs. Functional has some
interesting and even good answers to the problem of GUIs, but they all look
way klunkier in practice than they "actually are" in some sense because they
still have to work through those imperative toolkit bindings.

For a clean answer to function + event-driven, see Erlang, which will blow
pretty much any other attempt to solve the problems Erlang solves out of the
water. Functional doesn't have a problem with event-based, functional blows
imperative and OO out of the water at event-based. (Partially because it's a
lot easier to write sophisticated runtime like Haskell's or Erlang's that can
transparently deal with sync issues that drive imperative programs crazy.) Not
enough people are good enough at both OO and functional for this to be
commonly understood.

You'll know functional languages have entered the mainstream to stay when
someone writes a useful functional GUI.

~~~
anghyflawn
This is all true, but, in any case, whatever happened to MVC? After all, you
can (and arguably should) have your business logic happen in functional and
reduce the GUI or whatever (which might or might not be OO) to more or less a
thin layer of piping which ships data between the user and the pure parts of
the program.

------
tomp
I think that lightweight dynamic OO languages with first-class functions
(JavaScript, Lua) are an interesting middle point. They avoid some rigid
features of structural programming inherent to most other OO languages, such
as encapsulation and class definitions. Instead, objects serve the role of
generic data structures, that can also carry code (behaviour) with them.

I feel that a language that would allow both styles to mix even further
(immutable objects, pattern matching, tags (for ADTs), multimethods,
concurrency) would be really cool to use.

~~~
swift
Ruby belongs in the list you gave as well, I think, but they all have
tradeoffs, just in terms of the convenience of using first-class functions in
them. Ruby's block syntax (|x,y| x * y) is a lot more convenient than the more
verbose "function" syntax of JS and Lua for expressing inline functions (Lua:
function (x,y) return x*y end), pretty comparable to something like Haskell.
However, Ruby requires you to use either coroutine-style yield syntax or the
call() method to actually invoke a function value, while JavaScript and Lua
let you write parentheses after the value's name, in the same way that you
would call any other function.

For first-class functions to be convenient, minimally you need a terse syntax
for in-line functions, the ability to call function values as you would normal
functions, and closures. (this last, fortunately, all of the above support)
I'd argue that language support for currying is also extremely valuable and
difficult to live without, because it greatly increases the situations in
which a given function is useful. It's frustrating to me that mainstream
"scripting" languages like JS, Lua, and Ruby can't get all of these aspects
right - I hope for pattern matching and ADTs in a mainstream language, too,
but I'm still waiting for a good implementation of first-class functions to
arrive!

~~~
silentbicycle
FWIW, I wrote a pattern-matching library for Lua, Tamale
(<http://github.com/silentbicycle/tamale/>).

It was an interesting project, because almost everything I could find about
implementing pattern-matching assumed the patterns could be analyzed at
compile time, rather than building some kind of dispatch tree at runtime.
After trying some more elaborate methods (e.g. building a decision tree via
CPS), I figured out that just indexing (on the first field, by default) and
then doing linear search within the index's patterns got 90% of the benefit
with very little runtime analysis.

~~~
swift
Very cool!

------
ww520
This is off topic. Is there a language that automatically generates code when
the missing functions are referenced? It would be good for top-down style and
test-driven style programming.

E.g. I start with a high level flow of a program.

    
    
      int main() {
        outputData( processData( getData() ) );
      }
    

When compiled, the compiler realizes all the called functions are missing and
generates provisional stub code for them, with sensible default for parameters
and types.

    
    
      int @getData() {
         return 0;
      }
    
      int @processData(int param1) {
      }
    
      void @outputData(int param1) {
      }
    

The program can compile and run. I then go ahead to fill out the content of
the functions. The provisional functions are marked as such (the @ marker)
where the compiler can run in a validation mode to flag all functions not
implemented yet. When a function is done, the provisional marker can be
removed.

As more function calls are added, each compilation generates more provisional
functions, which can be filled out. Also as the completed function's
parameters and types become concrete, the compiler can update the provisional
caller or callee functions.

This kind of help from a compiler can really make test-driven development
easy. I can write the tests first that make calls to yet-to-exist functions
and the compiler generate the provisional version for me. And the tests run
right the way.

~~~
silentbicycle
In statically typed languages, that's usually avoided, because a typo in a
function name can escalate to subtle bugs.

Also, working with partial information complicates the type inference; in your
example, at best, it could infer () -> `a for getData, `a -> `b for
processData, and `b -> () for outputData.

In some languages (such as Smalltalk, Lua, and Ruby), there is an explicit
"message not understood" hook that is called when a nonexistent function is
referenced, and you can define stub behavior there.

~~~
ww520
Good point about the function name typo in static type language. Provisional
and complete functions are supposed to be different. The compiler can have a
validation mode to flag all provisional functions, which would catch the wrong
function name intended for a complete function.

The "message not understood" hook in dynamic languages is good for runtime
handling of missing functions, but I actually want the compiler to generate
the source code for the provisional functions, to make exploratory development
easier.

~~~
silentbicycle
IIRC, Haskell has some explicit marker for adding stub functions ("doThatStuff
x y z = undefined" or something like that). You still write the names for the
function and any arguments, though.

I tend to write code bottom-up rather than top-down, testing it in a REPL
and/or with tests, but have Emacs functions to generate boilerplate for
languages that need it.

------
VladRussian
functional programming naturally provides for reducing the complexity of the
system being modeled by the discovery of common patterns in the data and the
algorithms applied.

The foundation of OO approach is modeling system as it is with all its
complexity. This approach is cheaper, ie. easy on the brain, yet it results in
the much more unnecessary complex, bloated, system. The same, structurally,
code is multiplied through the system because it is bound to different data.
Please spare me lectures about how it is an incorrect approach - you'd insult
hundreds, if not thousands, of smart people whose source code i've seen during
the last almost 20 years. You can look yourself at any OO project in the open
source or at your workplace.

