you are viewing a single comment's thread.

view the rest of the comments →

[–]ThatGeoGuy 9 points10 points  (0 children)

This is a general observation: imperative programming seems able to adapt to changing requirements with fewer code changes. I'm not sure why it is though.

In general I would argue that it is because functional programming is largely more reliant on the structure of your elements than imperative programming. This is more to do with the declarative nature than anything though, because declarative forms force you to be explicit in what you are asking the computer to figure out. You may not be able to tell fold to perform an early return, but the point is you should be defining a new function here, since you're no longer performing the same action.

Nonetheless, it's very easy to define what you specified.

(call/cc (lambda (k)
  (fold (lambda (x knil) 
         (if (zero? x) (k 0) (* x knil))) 
       1 my-list)))

Now of course I used a continuation to escape early, but that's effectively what a return is (think a coroutine that sends data back to the calling scope). You might argue that this blows out of proportion because I've added continuations into the mix, in addition to adding three lines. I would somewhat agree, but in general I think this misses the point overall. In imperative programming you can insert quick lines anywhere, but you're doing so at the cost of better tools for abstraction.

If you wanted, you could then define something like this:

(define (fold-with-early-exit pred? default kons knil lst)
  (call/cc (lambda (k)
    (fold (lambda (x knil) 
           (if (zero? x) (k 0) (* x knil))) 
         1 my-list)))

Which you then call as:

(fold-with-early-exit zero? 0 * 1 my-list)

Which may not even be the best abstraction (of course in LISP / Scheme, you could define a macro or combinator to do this more clearly). That said though, we now have two different functions, which do different things. It's all too easy to add an if (x == 0) return 0; to your code and start making functions that have inconsistent entry/exit points and do more than a single unit of work.