12

If my function meets the two requirements listed below, I believe that the function Sum returns the summation of the items in a list, where item evaluates as true for a given condition. Doesn't this mean that the function can be classified as pure?

Requirements

  1. For given set of i/p, the same o/p is returned irrespective of when the function is called

  2. It does not have any side effects

    public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
        int result = 0;
        foreach(var item in numbers)
            if(predicate(item)) result += item;
        return result;
    }

Example : Sum(x=>x%2==0, new List<int> {1,2,3,4,5...100});

The reason I ask is that I see , almost everywhere, people advising to avoid the assignment operator and loops because it is characteristic of the imperative programming style.

So, what can go wrong with the above example which makes use of loops and the assignment operator in the context of function programming ?

2
  • 1
    It does not have any side effect - it have side effect, when item variable mutated in the loop.
    – Fabio
    Commented Oct 21, 2018 at 21:07
  • @Fabio ok. But can you elaborate on scope of side effect ? Commented Nov 28, 2018 at 23:09

4 Answers 4

19

What is it in functional programming that makes a difference?

Functional programming is by principle declarative. You say what your result is instead of how to compute it.

Let's take a look at really functional implementation of your snippet. In Haskell it would be:

predsum pred numbers = sum (filter pred numbers)

Is it clear what the result is? Quite so, it is sum of the numbers meeting the predicate. How is it computed? I don't care, ask the compiler.

You could possibly say that using sum and filter is a trick and it doesn't count. Let implement it without these helpers then (though the best way would be to implement them first).

The "Functional Programming 101" solution that doesn't use sum is with recursion:

sum pred list = 
    case list of
        [] -> 0
        h:t -> if pred h then h + sum pred t
                         else sum pred t

It is still pretty clear what is the result in terms of single function call. It is either 0, or recursive call + h or 0, depending on pred h. Still pretty straighforward, even if the end result is not immediately obvious (though with a little bit of practice this really reads just like a for loop).

Compare that to your version:

public int Sum(Func<int,bool> predicate, IEnumerable<int> numbers){
    int result = 0;
    foreach(var item in numbers)
        if (predicate(item)) result += item;
    return result;
}

What is the result? Oh, I see: single return statement, no surprises here: return result.

But what is result? int result = 0? Doesn't seem right. You do something later with that 0. Ok, you add items to it. And so on.

Of course, for most programmers this is pretty obvious what happens in a simple funciton like this, but add some extra return statement or so and it suddenly gets harder to track. All the code is about how, and what is left for the reader to figure out - this is clearly a very imperative style.

So, are variables and loops wrong?

No.

There are many things that are much easier explained by them, and many algorithms that require mutable state to be fast. But variables are inherently imperative, explaining how instead of what, and giving little prediction of what their value may be a few lines later or after a few loop iterations. Loops generally require state to make sense, and so they are inherently imperative as well.

Variables and loops are simply not functional programming.

Summary

Contemporarily funcitonal programming is a bit more of style and a useful way of thinking than a paradigm. Strong preference for the pure functions is in this mindset, but it's just a small part actually.

Most widespread languages allow you to use some functional constructs. For example in Python you can choose between:

result = 0
for num in numbers:
    if pred(num):
        result += num
return result

or

return sum(filter(pred, numbers))

or

return sum(n for n in numbers if pred(n))

These functional expressions fit nicely for that kind problems and simply makes code shorter (and shorter is good). You shouldn't thoughtlessly replace imperative code with them, but when they fit, they are almost always a better choice.

9
  • thx for nice explanation !! Commented Oct 21, 2018 at 14:50
  • 1
    @RahulAgarwal You may find this answer interesting, it nicely captures a tangential concept of establishing truths vs. describing the steps. I also like the phrase "declarative languages contain side effects while imperative languages do not" - usually functional programs has clean and very visible cuts between the stateful code dealing with outside world (or executing some optimized algorithm) and purely functional code.
    – Frax
    Commented Oct 25, 2018 at 22:00
  • 1
    @Frax : thanks !! I will look into it. Also recently came across Rich Hickey talk on Value of values It is really brilliant. I think one thumb rule - "work with values and expression rather than working with something holding value and can change" Commented Oct 26, 2018 at 6:59
  • 1
    @Frax : Also it is fair to say that FP is an abstraction over imperative programming - because ultimately someone have to instruct machine on "how to do", right ? If yes, then isn't imperative programming have more low level control compared to FP ? Commented Oct 27, 2018 at 13:36
  • 1
    @Frax: I would agree with Rahul that imperative is lower level in the sense that it is closer to the underlying machine. If hardware could make copies of data at no cost, we would not need destructive updates to improve efficiency. In this sense, the imperative paradigm is closer to the metal.
    – Giorgio
    Commented Oct 29, 2018 at 6:51
10

The use of mutable state is generally discouraged in functional programming. Loops are discouraged as a consequence, because loops are only useful in combination with mutable state.

The function as a whole is pure, which is great, but the paradigm of functional programming does not only apply at the level of whole functions. You also want to avoid mutable state also at the local level, inside functions. And the reasoning is basically the same: Avoiding mutable state makes the code easier to understand and prevents certain bugs.

In your case, you could write numbers.Where(predicate).Sum() which is clearly much simpler. And simpler means less bugs.

4
  • thx !! I think i was missing striking line - but the paradigm of functional programming does not only apply at the level of whole functions But now I am also wondering how to visualize this boundary. Basically from consumer perspective it is pure function but developer who actually wrote this function has not followed pure function guidelines ? confused :( Commented Oct 21, 2018 at 10:26
  • @RahulAgarwal: What boundary?
    – JacquesB
    Commented Oct 21, 2018 at 10:53
  • I am confused in sense if programming paradigm qualifies to be as FP from consumer of functions perspective ? Bcoz if i look at implementation implementation of Where in numbers.Where(predicate).Sum() - it makes use of foreach loop. Commented Oct 21, 2018 at 11:03
  • 4
    @RahulAgarwal: As a consumer of a function, you don't really care if a function or module internally uses mutable state as long is it is externally pure.
    – JacquesB
    Commented Oct 21, 2018 at 12:25
9

While you are correct that from an external observer's point of view, your Sum function is pure, the internal implementation is clearly not pure - you have state stored in result which you repeatedly mutate. One of the reasons to avoid mutable state is because it produces a greater cognitive load on the programmer, which in turn leads to more bugs[citation needed].

While in a simple example like this, the amount of mutable state being stored is probably small enough that it's not going to cause any serious issues, the general principle still applies. A toy example like Sum probably isn't the best way to illustrate the advantage of functional programming over imperative - try doing something with a lot of mutable state and the advantages may become clearer.

0

I see previous answers bang on about "mutability" and whether code is "declarative" or not.

I'd put the situation more simply.

Functional languages avoid loops mainly because they have a pre-defined library of methods which operate on arrays of values (I'll call these "array operators"), and which achieve the same effects as loops. Why prefer array operators over loops? Primarily because most loops consist of applying a well-defined algorithm or transformation, and array operators provide a simple way by which these transforms can be called by name. The alternative would be a hand-rolled loop with no explicit name.

As a fallback when there is no relevant array operator, functional languages provide some facility for recursive method calls. There is no point pretending: these are mechanically equivalent to loops.

Why prefer recursion over loops? The primary difference are the rules on variable scope and variable accessibility. Ordinary loops access variables declared outside the block - and the values of these remain available across each iteration and then also beyond the block itself.

With recursion however, the programmer must be explicit about which values are passed between iterations, and which values are returned. There is thus better locality. Although it must be said that the syntax of recursion (in existing languages), is often far less obvious and ergonomic than the typical syntax of loops.

Finally, why is assignment discouraged in functional programming? It isn't - fanatics argue about whether variables are "declared" or "assigned", but any sensible person can see that it's a difference without a distinction.

What is discouraged (or even disallowed) in functional languages is multiple assignment, and thus the reuse of variable names to refer to different values at different times (i.e. at different stages of execution).

In functional languages, only a method call is capable of changing the context where the same variable name can refer to a different value. And when this context changes, every variable in scope changes context (and anything preserved between contexts must be explicitly piped across as an argument to the method call).

By contrast, programmers in imperative languages tend to reuse variables extensively, and often in spatiotemporal patterns that are more complex than a Turkish rug. Again, the perceived advantage to single assignment is that it either encourages simplicity in these respects, or that it makes explicit just how complex and subtle the processing actually is.

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.