1. 11
  1.  

  2. 6

    What’s talked about very loosely (and rather specifically in Staltz’s Observables example) is that purity cannot be defined without discussion of how you distinguish things in your language. More to the point, it comes down to how you define that two programs are the same. A pure function has to present the same result whenever its arguments are the same (it has to “preserve equality”) and it must be absorbed by constants. In Haskell syntax this looks like

    f . const x = const (f x)
    const x . f = const x
    

    The first law says that x == y implies f x == f y because x == y applied to the law gives f . const x = const (f y) and then we can apply () to both sides and get, through the semantics of const, f x = f y. The second law basically says that the result of calling f is contained entirely in the result—if you throw the result away it’s the same as having thrown away the call to f entirely.

    These are both laws that depend upon being able to tell when two functions equal one another. Until you’ve defined that you cannot talk about purity.

    1. 12

      This is plain wrong. Just because you’re changing the variable each time you pass it in does not mean x => x *10; isn’t pure. You’re passing in different variables albeit in a sneaky way and complaining that it changed. The reality is that the value being passed to the function changes each time, the function is still pure.

      1. 12

        Exactly. Saying that your function produces different outputs because you pass Math.random to it as an argument isn’t saying your function is impure; it’s saying that Math.random is impure.

        The other examples are likewise invalid: passing an object with an overloaded valueOf method means that you’re changing your input and therefore should expect different output.

        1. 4

          Well… if you pass Math.random to a function, f, and it calls Math.random, then I’d have to say that f is impure. Math.random isn’t impure by itself—it’s a computation frozen in time, in other words: a value.

          The idea is that functions in programming languages glue together “functions” which take in values and produce values and merely exist and “procedures” which, when called to act, do something and can be put into sequences. What makes this difficult is that “calling” does dual purpose as “producing output from input” and “calling to act”.

          So if f is a function which is able to take a procedure in and call it to act and that procedure is anything more than just a constant-returning procedure, then f itself must also be a procedure and inherit side-effects from its argument.

          1. 3

            Would you argue that map is impure because it can take an impure function?

            1. 3

              I wouldn’t, but I’d argue it can be. It inherits the side effects of its argument.

              1. 3

                Yeah that’s not a very useful definition then. Since by your definition in javascript everything is indeterminate, and when everything is something it’s not a very useful word…

                1. 4

                  I think it’s a tremendously useful definition. The map function is pure when its argument is and not when its not.

                  Purity is powerful because it’s a tainting process. If you’ve got HOFs then they can be contaminated by their arguments.

                  1. 1

                    All javascript functions can accept an object, all objects have a value of, any javascript function accepting an object is impure because valueof can be impure, since we cannot know what types our functions accept until runtime we must assume that they are impure, since any number of things might pass an object to our function.

                    This implies all javascript functions not pure. If all javascript functions are not pure, the very word “impure function” is a tautological definition in the realm of javascript.

                    Tautological arguments don’t tend to be the cornerstone of productive thought.

                  2. 2

                    The reason why map isn’t a pure function in JavaScript is much simpler:

                    let twice = x => 2*x // hope we can agree this is pure
                    let xs = [1,2,3]
                    let ys = xs.map(twice)
                    let zs = xs.map(twice)
                    console.log(ys === zs) // prints false
                    

                    Object identities matter. You can mutate either ys or zs, and the changes won’t be reflected in the other.

                    In a language that emphasizes values over object identities, map is a pure function of its first argument: It takes as argument a function with effect f, and returns another function with effect f.

                    1. 1

                      That only returns false because [2,4,6] !== [2,4,6]

                      [2,4,6] !== [2,4,6] yields true

                      The fact that you can mutate ys or zs, has nothing to do with the purity of the function before it… Additionally given your definition of pure literally no function is pure in javascript. Given that “All functions are not pure”, impure function in your terms is a tautological definition.

                      1. 1

                        Functions that only manipulate booleans, numbers and strings (and nothing else!) could be pure. Admittedly, this isn’t an interesting enough subset of JavaScript to actually write programs in.

              2. 2

                In a higher-order language, I think effect polymorphism is a more useful approach than a rigid pure vs. effectful distinction. For example, it would be nice if map had type forall (a b :: Type) (f :: Effect). (a -f-> b) -> [a] -f-> [b]. Then you wouldn’t need to separately define traverse / mapM.

                1. 1

                  Definitely in that it completely describes when a function is pure as a function of its inputs. This will always arise when one does not distinguish between doing and being.

                  1. 1

                    Call-by-value languages already distinguish between values (being) and computations (doing). Arbitrary terms denote computations, but free variables can only be substituted with terms that denote values.

                    Going even further, call-by-push-value languages reflect the value-computation distinction in types. Positive types are inhabited by values, negative types are inhabited by computations, and positive and negative types inhabit different kinds.

                    Static effect systems don’t distinguish between values and computations. In fact, effects aren’t concerned with values at all. Effects classify computations according to what they may do, besides taking a finite amount of time to run and returning a successful result.

                    1. 1

                      Eh, I don’t think we’re actually arguing at all :)

            2. 4

              You fail to see that it all depends on the definition of “pure”, just as the article explained.

              1. 3

                When your definition makes all functions indeterminate, it’s a pretty bad definition. The only time that would be useful is comparing with a different language, but I think it can be healthily assumed that if you’re writing in javascript without any type safety it’s because that’s your only option.

              2. 2

                To put a fine point on it, const f = x => x * 10 references x which “desugars” to calling/executing x.valueOf. Since f thus calls/executes an impure function/procedure it is/has the capacity to be impure. It might be the case that f doesn’t actually call x.valueOf but instead x.valueOf is called in the making of the call to f and its result passed in (I honestly don’t know) but since there is no way to distinguish these two events we must still consider them equivalent.

              3. 1

                You tell me: a => a + 1

                Looks pure to me

                Where is my treat