1

I'm sure this must have been asked before, but I can't find anywhere that actually answers my question, so apologies if I have simply overlooked this.

I am currently learning Haskell, and loving the way it balances flexibility allowing rapid prototyping with functional purity. However I'm trying to get my head around best practices. One thing I'm not currently sure of is, say you have something like the following:

worlds :: [Foo] -> [World]
worlds [] = []
worlds fs = map toWorld fs

This function relies on a toWorld function that would have been defined elsewhere. My immediate reaction here is that because you are interacting with something "global", is it "correct" to do this? Or would you better to pass toWorld into the function. Now, say this was Javascript, obviously you could do something like:

function toWorld (foo) {
    console.log('Side effect');
    return foo; // The actual transformation is irrelevant
}

In this case, my understanding is that the purity of worlds is simply dependent on the purity of toWorld, and with an implementation such as this, worlds would become impure because toWorld has a side-effect. Of course in Haskell, where such side-effects are impossible, you are guaranteed that toWorld will always be pure, so this "makes it okay"? (If that makes sense)

Furthermore, say that toWorld not only took a foo, but also took some constant that influenced the transformation. So you had something like:

constant = 5

toWorld :: Int -> Foo -> World
toWorld k f = World k f -- Something, again, irrelevant

worlds :: [Foo] -> [World]
worlds [] = []
worlds fs = map (toWorld constant) fs

Is this now "impure"? Should the constant be something that is always passed to worlds? Because surely someone could just redefine constant to something else at runtime and change how the function works?

Hope all that makes sense. Wanted to get some thoughts out. Any input would appreciated, no matter how small. Cheers!

6

Is this now "impure"? Should the constant be something that is always passed to worlds?

In your second example, the function worlds is actually a closure that references the variable constant defined in its context. This does not influence its purity because worlds still respects referential transparency, i.e. you can replace any invocation worlds fs by its result without changing the semantics of the program.

So, after capturing the global name constant, world is frozen into a pure function.

Because surely someone could just redefine constant to something else at runtime and change how the function works?

You cannot redefine a constant at runtime in Haskell: once a constant has been compiled, it retains its value. There is no such thing as an assignment statement in Haskell. You also cannot redefine a constant in the source code because the compiler will complain: Multiple declarations of ...

However, you can apparently redefine a constant in the REPL using let. For example:

Prelude> let x = 1
Prelude> x
1
Prelude> f y = y + x
Prelude> f 6
7
Prelude> let x = 3
Prelude> x
3

Now you might think that x has the new value 3 and this could influence f, but in fact you have defined two xs and the second one has shadowed the first one. The closure f still sees the x that is in scope where f is defined, so now you still have

Prelude> f 6
7

because f is frozen the moment it is defined.

3

Constants, be they other function definitions or data constants, don't influence purity or functional design.

1

No, this has nothing to do with function purity. The operation that toWorld performs could just as well have been defined within worlds and then the question would not even arise. The fact that it's a separate function doesn't change anything about that. Function pureness is about not depending on implicit data values, not about not depending on other functions.

  • Even if those other functions depend on implicit data values? – candied_orange Sep 23 '18 at 13:39
  • @candied_orange a function that invokes an impure function is impure, whether that function is passed as a parameter or is global. – Jules Sep 23 '18 at 18:12
  • @Jules that sounds better. Not sure implicit vs explicit is really the best way to explain side effects. – candied_orange Sep 23 '18 at 18:43
  • @candied_orange: "Not sure implicit vs explicit is really the best way to explain side effects.": It is not, e.g. in C++ you can pass an object by reference (&) and then modify it. The function or method that changes the object is not pure. – Giorgio Sep 27 '18 at 5:26

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.