Question

Since the purity of an input parameter is an unknown until runtime, is a function immediately considered impure if it takes a function as an input parameter?

Related: if a function applies a pure function that is defined outside of the function, but is not passed in as a parameter, is it still pure if it fulfills the criteria of having no side effects and output depends solely on input?

For context, I'm writing functional code in JavaScript.

Was it helpful?

Solution

As long as all values used in the function are defined solely by its parameters, it's a pure function.

The facet that output is the same each time for the same input is controlled by whether the parameters are pure. If you assume the parameters (like a function argument) are also pure, then it is pure.

In a language like Javascript where purity isn't enforced, this means that it's possible to make an otherwise pure function have impure behavior by invoking an impure function passed as a parameter.

This effectively means that for languages that don't enforce purity (ie almost all), it's impossible to define a pure function which invokes functions passed as arguments. It's still useful to write them as pure as possible, and to reason about them as pure functions, but you have to exercise caution because the assumption that it's pure will be broken if you pass in the wrong arguments.

In my experience in practice this isn't usually a big deal - I find it rare to have impure functions be used as function arguments to pure functions.

OTHER TIPS

Since the purity of an input parameter is an unknown until runtime, is a function immediately considered impure if it takes a function as an input parameter?

No. Counterexample:

function pure(other_function) {
    return 1;
}

It doesn't matter whether other_function is a pure function, an impure function, or not a function at all. The pure function is pure.

Other counterexample:

function identity(x) {
    return x;
}

This function is pure, even if x is an impure function. identity(impure_function) will always return impure_function, no matter how many times you repeat the call. It doesn't matter whether identity(impure_function)() always returns the same thing; a function's return value's return value doesn't affect its purity.


In general, if a function might call a function it was passed as an argument, it's not pure. For example, a function function call(f) {f();} isn't pure, because even though it makes no mention of any global or mutable state, f might be something like alert that causes visible side effects.

If a function takes functions as arguments, but it doesn't call them or cause them to be called, then it can be pure. It might still be impure if it does some other impure thing. For example, function f(ignored_function) {alert('This isn't pure.');} is impure, even though it never calls ignored_function.

Since the purity of an input parameter is an unknown until runtime, is a function immediately considered impure if it takes a function as an input parameter?

Technically, yes, unless there is some way in your language to guarantee that the input function is also pure.

if a function applies a pure function that is defined outside of the function, but is not passed in as a parameter, is it still pure if it fulfills the criteria of having no side effects and output depends solely on input?

Yes. So let's focus on what matters here. Calling a function pure or not isn't in and of itself useful. Pure functions are useful because producing the same output for any input and not depending on state or having side effects is a very useful set of properties. It means that once your function has been run, you can "remember" the answer for that input and it will always be true. You also don't need to run the function again to generate side effects. And you can run that function in parallel (or out of order) with other functions and know that they won't have any hidden interactions that behave badly.

Those useful properties still hold if the function uses other pure read-only functions to do its work, regardless of how it references them.

As Telastyn said: Technically, yes, unless there is some way in your language to guarantee that the input function is also pure.

That's not hypothetical, there are indeed good ways to guarantee this. At least in a strongly typed language.

Such a pure~ function you'd write in JavaScript as

function foo(f) {
   return f(1) + 2;
}

can be translated directly to Haskell:

foo :: (Int -> Int) -> Int
foo f = f 1 + 2

Now, in JavaScript you can do evil stuff like

js> foo (function(x) {console.log("muharhar"); return 0})
muharhar
2

This is not possible in Haskell. The reason being, something side-effect-ful like console.log() must always have a result type IO something, not just something alone.

GHCi> foo (\x -> print "muarhar" >> return 0)

<interactive>:7:12:
    Couldn't match expected type ‘Int’ with actual type ‘IO b0’
    In the expression: print "muarhar" >> return 0
    In the first argument of ‘foo’, namely
      ‘(\ x -> print "muarhar" >> return 0)’
    In the expression: foo (\ x -> print "muarhar" >> return 0)

For this expression to typecheck, we would need to give foo the type signature

foo :: (Int -> IO Int) -> Int

But it turns out I cannot implement it anymore then: because the argument function has IO in its result, I can not use it within foo.

<interactive>:8:44:
    Couldn't match expected type ‘Int’ with actual type ‘IO Int’
    In the first argument of ‘(+)’, namely ‘f 1’
    In the expression: f 1 + 2

The only way I could use an IO action in foo is if the result of foo has type IO Int itself:

foo :: (Int -> IO Int) -> IO Int
foo f = do
   f1 <- f 1
   return (f1 + 2)

But at this point it's clear from the signature of foo that it is not a pure function, either.

No it is not.

If the passed function is impure AND your function calls the passed function then your function will be considered impure.

The pure/impure relation is a bit like sync/async in JS. You can freely use pure code from impure, but not the other way around.

Licensed under: CC-BY-SA with attribution
scroll top