1

I know that a pure function is a function that doesn't rely on system's state, doesn't have side effects, and its output only depends on its inputs.

Making an http call is considered to be a side effect. So, the following is an example of an impure function: const httpCall = (url, params) => $.getJson(url, params)

But, just delaying evaluation, we can transform that function into an pure one, as follows: const pureHttpCall = (url, params) => () => $.getJson(url, params)

We are not making the http call anymore. Instead, we are returning a function that will do so when called. This function is pure because it will always return the same output given the same input: the function that will make that particular http call given the url and params.

But this confuses me.. Because when we call this returned function, we will be making the http call anyway. I don't see how this "delayed evaluation" is removing impurity in our system.

I think I need to clarify some of this concepts, because I don't get how side effects (like http calls) fits in the functional paradigm.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
Nacho
  • 21
  • 1
  • 1
    Function purity does not take into account deeper return values. All it cares about is the return value of `pureHttpCall`, which is the same given the same inputs: a function that makes an HTTP request. The returned function is the one that is not pure. – Andrew Li Aug 11 '19 at 21:36
  • 1
    @Li357 So.. can I say that `pureHttpCall` is pure and its returned value is an impure function? – Nacho Aug 11 '19 at 21:38
  • Yes. The returned function is the one that actually executes the side effects while the pure one does not. – Andrew Li Aug 11 '19 at 21:39
  • @Li357 Thats how you normally deal with side effects and impure stuff in functional programming? – Nacho Aug 11 '19 at 21:39
  • Essentially yes. Just pass around the expressions without evaluating them (i.e. lazy evaluation), and only evaluate them once you're in an impure environment. – Unlocked Aug 11 '19 at 21:46
  • Related: https://stackoverflow.com/questions/56193873 . – atravers Jan 13 '22 at 01:37

1 Answers1

1

So, you think that the following function is impure.

const pureHttpCall = (url, params) => () => $.getJson(url, params)

That's a reasonable thought. However, would you consider the following function pure?

const pureHttpCall = (url, params) => ({ url, params })

The second function doesn't make any HTTP call. It just returns a pure computation object.

You can eventually use this pure computation object to run impure code. For example, consider.

const runIO = ({ url, params }) => $.getJson(url, params)

const pureHttpCall = (url, params) => ({ url, params })

const computation = pureHttpCall("example.json", {}) // create a pure computation

runIO(computation) // use the pure computation to run impure code

Think of the pure computation object as a description of impure code. The description is pure but the thing it describes is impure. We're using a computation object data structure as a description for impure code.

Now, a function is also a data structure. After all, functions are first-class values in FP. Hence, instead of using an object as the description data structure, we can use a nullary function as follows.

const runIO = computation => computation()

const pureHttpCall = (url, params) => () => $.getJson(url, params)

const computation = pureHttpCall("example.json", {}) // create a pure computation

runIO(computation) // use the pure computation to run impure code

To summarize, $.getJson(url, params) is an impure HTTP call. However, () => $.getJson(url, params) is a pure description of an impure HTTP call.

If the fact that the description is an impure function is what's bothering you, then think of the function as a pure computation object like I showed above.

Converting functions into data structures like I did above is actually a compiler optimization technique known as defunctionalization.


This is how purely functional languages like Haskell deal with impurity. They wrap impure code in pure IO actions. An IO action is a description of impure code. Internally the IO action is defined by a state monad data structure where the state is the RealWorld[1].

newtype IO a = IO (State# RealWorld -> (# State# RealWorld, a #))

The general idea is that you build a description of impure code (i.e. an IO action) and then when you run the program the Haskell runtime will run the main action. You can also use unsafePerformIO to run IO actions beside main, and it is usually used to get configuration data.


Note that there's no such thing as “compile-time purity” and “runtime impurity” as @bob's answer says. Separation of purity and impurity is done by pure data structures which describe impure computations. You can do that in interpreted languages like JavaScript too as I showed above.

Compile time and runtime do not separate purity from impurity. Compile time simply refers to operations performed by the compiler, such as macro expansion or type checking. Similarly, runtime refers to operations performed by the program. The compile time of a program is the runtime of the compiler.

Note that parts of the program can also be executed at compile time. For example, function inlining and macro expansion is userland code being executed at compile time. In dependently typed languages, parts of the program are also evaluated at compile time for the purpose of type checking.

Aadit M Shah
  • 72,912
  • 30
  • 168
  • 299
  • The evaluation order of a Haskell program isn't always determined by the lexical strcuture of its code because of purity. The compiler can perform equational reasoning to optimize code. This is what I meant with "compile-time purity". `main` in Haskell is the entry point of each program and is of type `IO ()`. `main` is interpreted at run time along with all impure actions it contains. The evaluation order is and have to be determined by applicative functors and monads, because the run time interprets impure code. That is what I was referring to as "run-time impurity". –  Mar 11 '20 at 11:50
  • Can you be more specific about where my conclusions are wrong? –  Mar 11 '20 at 11:50
  • 1
    @bob https://gist.github.com/adit-hotstar/3ecf2a3fcc3c48585c514563b3b2e4f1 – Aadit M Shah Mar 11 '20 at 15:03