views:

220

answers:

3

I'm learning functional programming, and have tried to solve a couple problems in a functional style. One thing I experienced, while dividing up my problem into functions, was it seemed I had two options: use several disparate functions with similar parameter lists, or using nested functions which, as closures, can simply refer to bindings in the parent function.

Though I ended up going with the second approach, because it made function calls smaller and it seemed to "feel" better, from my reading it seems like I may be missing one of the main points of functional programming, in that this seems "side-effecty"? Now granted, these nested functions cannot modify the outer bindings, as the language I was using prevents that, but if you look at each individual inner function, you can't say "given the same parameters, this function will return the same results" because they do use the variables from the parent scope... am I right?

What is the desirable way to proceed?

Thanks!

+1  A: 

Functional programming isn't all-or-nothing. If nesting the functions makes more sense, I'd go with that approach. However, If you really want the internal functions to be purely functional, explicitly pass all the needed parameters into them.

Here's a little example in Scheme:

(define (foo a)
  (define (bar b)
    (+ a b))      ; getting a from outer scope, not purely functional
  (bar 3))

(define (foo a)
  (define (bar a b)
    (+ a b))      ; getting a from function parameters, purely functional
  (bar a 3))


(define (bar a b) ; since this is purely functional, we can remove it from its
  (+ a b))        ; environment and it still works

(define (foo a)
  (bar a 3))

Personally, I'd go with the first approach, but either will work equally well.

Kyle Cronin
+1  A: 

Nesting functions is an excellent way to divide up the labor in many functions. It's not really "side-effecty"; if it helps, think of the captured variables as implicit parameters.

One example where nested functions are useful is to replace loops. The parameters to the nested function can act as induction variables which accumulate values. A simple example:

let factorial n =
    let rec facHelper p n =
        if n = 1 then p else facHelper (p*n) (n-1)
    in
    facHelper 1 n

In this case, it wouldn't really make sense to declare a function like facHelper globally, since users shouldn't have to worry about the p parameter.

Be aware, however, that it can be difficult to test nested functions individually, since they cannot be referred to outside of their parent.

Jay Conrod
facHelper doesn't actually refer to any external values - it's still purely functional.
Kyle Cronin
+1  A: 

Consider the following (contrived) Haskell snippet:

putLines :: [String] -> IO ()
putLines lines = putStr string
    where string = concat lines

string is a locally bound named constant. But isn't it also a function taking no arguments that closes over lines and is therefore referentially intransparent? (In Haskell, constants and nullary functions are indeed indistinguishable!) Would you consider the above code “side-effecty” or non-functional because of this?

Matthias Benkard