Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

After learning functional programming in Haskell and F#, the OOP paradigm seems ass-backwards with classes, interfaces, objects. Which aspects of FP can I bring to work that my co-workers can understand? Are any FP styles worth talking to my boss about retraining my team so that we can use them?

Possible aspects of FP:

  • Immutability
  • Partial Application and Currying
  • First Class Functions (function pointers / Functional Objects / Strategy Pattern)
  • Lazy Evaluation (and Monads)
  • Pure Functions (no side effects)
  • Expressions (vs. Statements - each line of code produces a value instead of, or in addition to causing side effects)
  • Recursion
  • Pattern Matching

Is it a free-for-all where we can do whatever the programming language supports to the limit that language supports it? Or is there a better guideline?

share|improve this question
6  
I had a similar experience. After about 2 months of pain I started to find quite a nice balance of "stuff that maps to objects" and "stuff that maps to functions". It helps to do some serious hacking in a language that supports both. At the end of it, both my FP and OOP skills are vastly improved –  jozefg Nov 13 '13 at 4:05
3  
FWIW, Linq is both functional and lazy, and you can simulate functional programming in C# by using static methods and avoiding state persistence. –  Robert Harvey Nov 13 '13 at 4:28
1  
At this point, you should read sicp. It is free and well written. It offers a nice comparison between the two paradigms. –  Simon Nov 13 '13 at 8:44
4  
FP and OOP are at the same time in some sense orthogonal and in some sense dual. OOP is about data abstraction, FP is about (the absence of) side-effects. Whether or not you have side-effects is orthogonal to how you abstract your data. Lambda Calculus is both functional and object-oriented for example. Yes, FP typically uses Abstract Data Types, not objects, but you could just as well use objects instead without being any less FP. OTOH, there is also a deep relation: a function is isomorphic to an object with only one method (that's how they are "faked" in Java, and implemented in Java8, … –  Jörg W Mittag Nov 13 '13 at 10:52
3  
I think the strongest aspect of your question has to do with readability. "How much Functional Programming style is appropriate to bring to work at an Object Oriented shop?" Or what Functional features are worth a little OOP confusion for the benefits they bring. –  GlenPeterson Nov 13 '13 at 15:09

6 Answers 6

Functional programming is a different paradigm from Object-Oriented programming (a different mindset, and a different way of thinking about programs). You have begun to realize that here is more than one way (object-oriented) to think about problems and their solutions. There are others (procedural and generic programming come to mind). How you react to this new knowledge, whether you accept and integrate these new tools and approaches into your skill set, will determine whether you grow and become a more complete, skilled developer.

We are all trained to handle and are comfortable with a certain level of complexity. I like to call this a person's hrair limit (from Watership Down, how high can you count). It is a great thing to expand your mind, your ability to consider more options, and have more tools to approach and solve problems. But it is a change, and it pulls you out of your comfort zone.

One problem you may encounter is that you will become less content to follow the "everything is an object" crowd. You may have to develop patience as you work with people who may not understand (or want to understand) why a functional approach to software development works well for certain problems. Just as a generic programming approach works well for certain problems.

Good Luck!

share|improve this answer
2  
I would like to add, that one can gain a better understanding of some traditional OOP concepts when working with functional languages such as Haskell or Clojure. Personally I realized how polymorphism is really an important concept (Interfaces in Java or typeclasses in Haskell), while inheritance (what I thought to be a defining concept) is kind of a weird abstraction. –  wirrbel Nov 13 '13 at 12:34

You may have to constrain what parts of your knowledge you use at work, the way Superman has to pretend to be Clark Kent in order to enjoy the perks of a normal life. But knowing more will never hurt you. That said, some aspects of Functional Programming are appropriate for an Object Oriented shop, and other aspects may be worth talking to your boss about so that you can raise the average knowledge level of your shop and get to write better code as a result.

FP and OOP are not mutually exclusive. Look at Scala. Some think it's the worst because it is impure FP, but some think it is the best for that same reason.

One by one, here are some aspects that work great with OOP:

  • Pure Functions (no side effects) - Every programming language I know of supports this. They make your code much, much easier to reason about and should be used whenever practical. You don't have to call it FP. Just call it good coding practices.

  • Immutability: String is arguably the most commonly used Java object and it is immutable. I cover Immutable Java Objects and Immutable Java Collections on my blog. Some of that may be applicable to you.

  • First Class Functions (function pointers / Functional Objects / Strategy Pattern) - Java has had a hobbled, mutant version of this since version 1.1 with most of the API classes (and there are hundreds) that implement the Listener interface. Runnable is probably the most commonly used functional object. First Class Functions are more work to code in a language that doesn't support them natively, but sometimes worth the extra effort when they simplify other aspects of your code.

  • Recursion is useful for processing trees. In an OOP shop, that is probably the primary appropriate use of recursion. Using recursion for fun in OOP should probably be frowned upon if for no other reason than most OOP languages don't have the stack space by default to make this a good idea.

  • Expressions (vs. Statements - each line of code produces a value instead of, or in addition to causing side effects) - The only evaluative operator in C, C++, and Java is the Ternary Operator. I discuss appropriate usage on my blog. You may find you write some simple functions that are highly reusable and evaluative.

  • Lazy Evaluation (and Monads) - mostly restricted to lazy Initialization in OOP. Without language features to support it, you may find some APIs that are useful, but writing your own is difficult. Maximize your use of streams instead - see the Writer and Reader interfaces for examples.

  • Partial Application and Currying - Not practical without first class functions.

  • Pattern Matching - generally discouraged in OOP.

In summary, I do not think work should be a free-for-all where you can do whatever the programming language supports to the limit that language supports it. I think readability by your coworkers should be your litmus test for code made for hire. Where that chafes you the most I would look into starting some education at work to broaden the horizons of your co-workers.

share|improve this answer
    
Since learning FP I've habitted to design things to have fluent interfaces which results in what is akin to expressions, a function that has one statement which does a bunch of things. It's the closest you'll really get but it's an approach that flows naturally from purity when you find you no longer have any void methods, using static extension methods in C# aids this greatly. In that way your expression point is the only point I disagree with, everything else is spot on with my own experiences learning FP and working a .NET day job –  Jimmy Hoffa Nov 13 '13 at 16:13
    
What really bothers me in C# now is that I cannot use delegates instead of one-method interfaces for 2 simple reasons: 1. you cannot make recursive lambas without a hack (assigning to null first and then to a lambda second) or a Y-combinator (which is as ugly as hell in C#). 2. there are no type aliases that you can use in a project scope, so the signatures of your delegates pretty quickly become unmanageable. So just for these 2 stupid reasons I cannot enjoy C# anymore, because the only thing I can make it work is by using one-method interfaces which is just unnecessary extra work. –  bonomo Nov 13 '13 at 16:52
    
@bonomo Java 8 has a generic java.util.function.BiConsumer which might be useful in C#: public interface BiConsumer<T, U> { public void accept(T t, U u); } There are other useful functional interfaces in java.util.function. –  GlenPeterson Nov 13 '13 at 17:15
    
@bonomo Hey I get it, this is the pain of Haskell. Whenever you read someone say "Learning FP made me better at OOP" means they learned Ruby or something that's not pure and declarative like Haskell. Haskell makes it clear OOP is uselessly low level. The biggest pain you're running into is that constraint-based type inference is undecidable when you're not in the HM type system, so cosntraint-based type inference is completely not done: blogs.msdn.com/b/ericlippert/archive/2012/03/09/… –  Jimmy Hoffa Nov 13 '13 at 17:16
1  
Most OOP languages don't have the stack space for it Really? All you would need is 30 levels of recursion to manage billions of nodes in a balanced binary tree. I'm pretty sure my stack space is suitable for many more levels than this. –  Robert Harvey Nov 13 '13 at 17:25

Functional programming yields very practical, down-to-earth, productivity in everyday's code writing: some features favour terseness, which is great because the less code you write, the less failures you do, and the less maintenance is required.

Being a mathematician, I find fancy functional stuff very appealing, but it is usually useful when designing an application: these structures can encode in the program structure a lot of invariants of the program, without representing these invariants by variables.

My favourite combination may look pretty trivial, I however believe that it has a very high productivity impact. This combination is Partial Application and Currying and First Class Functions which I would relabel never write a for-loop again: instead pass the body of the loop to an iterating or mapping function. I was recently hired for a C++ job and I amusingly noticed, I totally lost the habit of writing for-loops!

The combination of Recursion and Pattern Matching annihilates the need of that Visitor design pattern. Just compare the code you need to program an evaluator of boolean expressions: in any functional programming language this should be about 15 lines of code, in OOP the right thing to do is to use that Visitor design pattern, which turns that toy-example in an extensive essay. Advantages are obvious and I am not aware of any inconvenient.

share|improve this answer
1  
I agree completely but I have had push-back from folks who throughout the industry tend to agree: They know they visitor pattern, they've seen and used it many times so the code in it is something they understand and are familiar with, the other approach though ridiculously simpler and easier is foreign and therefore more difficult to them. This is an unfortunate fact of the industry having had 15+ years of OOP banged into every programmers head that 100+ lines of code is easier for them to understand than 10 simply because they've memorized that 100+ lines after repeating them over a decade –  Jimmy Hoffa Nov 13 '13 at 17:22
1  
-1 - More terse code does not mean you're writing "less" code. You're writing the same code using less characters. If anything, you make more errors because the code is (often) harder to read. –  Telastyn Nov 13 '13 at 18:11
6  
@Telastyn: Terse is not the same as unreadable. Also, huge masses of bloated boilerplate have their own way of being unreadable. –  Michael Shaw Nov 13 '13 at 21:15
1  
@Telastyn I think you just touched on the real crux here, yes terse can be bad and unreadable, bloated can be bad and unreadable too, but the key isn't the variable length and puzzlically written code. The key is as you mention above number of operations, I disagree that number of operations doesn't correlate to maintainability, I think doing less things (with clearly written code) does benefit readability and maintainability. Obviously doing the same number of things with single letter function and variable names won't help, good FP needs considerably less operations still clearly written –  Jimmy Hoffa Nov 14 '13 at 15:59
2  
@user949300: if you want a completely different example, how about this Java 8 example?: list.forEach(System.out::println); From an FP point of view, println is a function taking two arguments, a target PrintStream and a value Object but the Collection’s forEach method expects a function with one argument only which can be applied to every element. So the first argument is bound to the instance found in System.out yielding to a new function with one argument. It’s simpler than BiConsumer<…> c=PrintStream::println; PrintStream a1=System.out; list.forEach(a2 -> c.accept(a1, a2)); –  Holger Nov 15 '13 at 9:30

In addition to functional programming and object oriented programming, there's also declarative programming (SQL, XQuery). Learning each style helps you gain new insights, and you'll learn to pick the right tool for the job.

But yeah, it can be very frustrating to write code in a language, and know that if you were using something else, you could be way more productive for a particular problem domain. However, even if you're using a language like Java, it is possible to apply concepts from FP to your Java code, albeit in roundabout ways. The Guava framework for instance does some of this.

share|improve this answer

As a programmer I think you should never stop learning. That said, it's very interesting that learning FP is tainting your OOP skills. I tend to think of learning OOP as learning how to ride a bike; you never forget how to do it.

As I learned the ins and outs of FP, I found myself thinking more mathematically and gained a better perspective of the means in which I write software. That's my personal experience.

As you gain more experience, core programming concepts will be much harder to lose. So I suggest you take it easy on the FP until OOP concepts are totally solidified in your mind. FP is a definite paradigm shift. Good luck!

share|improve this answer
4  
Learning OOP is like learning to crawl. But once you get on your feet steadily, you'll only resort to crawling when you're too drunk. Of course you cannot forget how to do it, but you would not normally want to. And it will be a painful experience to walk with the crawlers when you know you can run. –  SK-logic Nov 13 '13 at 10:15
    
@SK-logic, I like your methaphor –  bonomo Nov 13 '13 at 15:11
    
@SK-Logic: What is learning imperative programming like? Dragging yourself along on your stomach? –  Robert Harvey Nov 13 '13 at 17:22
    
@RobertHarvey trying to burrow underground with a rusty spoon and a deck of punchcards. –  Jimmy Hoffa Nov 13 '13 at 20:59

There are many good answers already, so mine will address a subset of your question; namely, I take umbrage to the premise of your question, as OOP and functional features aren't mutually exclusive.

If you use C++11, there are a lot of these sorts of functional-programming features built into the language/standard library that synergize (pretty) well with OOP. Of course, I'm not sure how well TMP will be received by your boss or coworkers, but the point is you can get many of these features in some form or another in non-functional/OOP languages, like C++.

Using templates with compile time recursion relies on your first 3 points,

  • Immutability
  • Recursion
  • Pattern Matching

In that template-values are immutable (compile-time constants), any iteration is done using recursion, and branching is done using (more or less) pattern matching, in the form of overload resolution.

As for the other points, using std::bind and std::function gives you partial function application, and function pointers are built-in to the language. Callable objects are functional objects (as well as partial function application). Note that by callable objects, I mean ones that define their operator ().

Lazy evaluation and pure functions would be a bit harder; for pure functions, you can use lambda functions which only capture by value, but this is non-ideal.

Lastly, here's an example of using compile-time recursion with partial function application. It's a somewhat contrived example, but it demonstrates most of the points above. It'll recursively bind the values in a given tuple to a given function and generate a (callable) function object

#include <iostream>
#include <functional>

//holds a compile-time index sequence
template<std::size_t ... >
struct index_seq
{};

//builds the index_seq<...> struct with the indices (boils down to compile-time indexing)
template<std::size_t N, std::size_t ... Seq>
struct gen_indices
  : gen_indices<N-1, N-1, Seq ... >
{};

template<std::size_t ... Seq>
struct gen_indices<0, Seq ... >
{
    typedef index_seq<Seq ... > type;
};


template <typename RType>
struct bind_to_fcn
{
    template <class Fcn, class ... Args>
    std::function<RType()> fcn_bind(Fcn fcn, std::tuple<Args...> params)
    {
        return bindFunc(typename gen_indices<sizeof...(Args)>::type(), fcn, params);
    }

    template<std::size_t ... Seq, class Fcn, class ... Args>
    std::function<RType()> bindFunc(index_seq<Seq...>, Fcn fcn, std::tuple<Args...> params)
    {
        return std::bind(fcn, std::get<Seq>(params) ...);
    }
};

//some arbitrary testing function to use
double foo(int x, float y, double z)
{
    return x + y + z;
}

int main(void)
{
    //some tuple of parameters to use in the function call
    std::tuple<int, float, double> t = std::make_tuple(1, 2.04, 0.1);                                                                                                                                                                                                      
    typedef double(*SumFcn)(int,float,double);

    bind_to_fcn<double> binder;
    auto other_fcn_obj = binder.fcn_bind<SumFcn>(foo, t);
    std::cout << other_fcn_obj() << std::endl;
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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