13

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?

GlenPeterson
  • 14,950
Trident D'Gao
  • 441
  • 4
  • 13

6 Answers6

13

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!

6

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.

user40989
  • 2,940
5

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.

GlenPeterson
  • 14,950
3

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.

2

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!

0

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;
}
alrikai
  • 101