0

I'm learning functional programming. I have a very specific question. Technically can a class method be considered a pure function? Let's say we have an add method: add(a, b) { return a + b }. Is the add method a pure function or should it be declared outside of the class to be considered as a pure function?

Florian G
  • 567
  • 1
  • 5
  • 15
  • 4
    Sure, why wouldn't it? – Bergi Jun 02 '21 at 14:02
  • 2
    As long as you don't make any reference to `this` or to external variables, then yes. – Scott Sauyet Jun 02 '21 at 14:05
  • 1
    @ScottSauyet You can even reference `this` and use its properties, as long as you don't mutate them – Bergi Jun 02 '21 at 14:07
  • @Bergi: hmmm. I think I disagree. That function can be called with a different `this` context, even if it's seen as a method of an object. `const myFn = myObj.myFn; myFn(11, 31)`. If there is a `this` reference in `myFn`, this use could well have a different result than would `myObj.myFn(11, 31)`. – Scott Sauyet Jun 02 '21 at 14:11
  • @ScottSauyet I consider the receiver of the method call to be the zeroth argument, as input to the function. Calling a pure function with different inputs may of course have different results. – Bergi Jun 02 '21 at 14:14
  • @Bergi: while that's reasonable, there's still the question of object identity. Even if *you* don't mutate the object, it could have been mutated between calls. You would have to treat each mutation as a separate zeroth argument to consider the function pure. `class Cart {... add (items) {return this.discount * sum (items.map(i => i.price)) } ...} ... const myCart = newCart(); myCart.discount = 1; myCart.add(items) //=> 42. myCart.discount = .9; myCart.add(items) //=> 37.8`. While I guess you could consider this a different object from the function's perspective, that feels... odd. – Scott Sauyet Jun 02 '21 at 14:23
  • 1
    @ScottSauyet That argument holds for *any* mutable object that a function receives as input, regardless whether it's a method of that object or not. – Bergi Jun 02 '21 at 14:38
  • @bergi: But then wouldn't it be true that *every* JS function should be considered to have a potential zeroth argument? Would you call `(a, b) => (this?.discount ?? 1) * (a + b)` a pure function, even though it has different results based on the value of `global.discount`? That still seems strange to me. – Scott Sauyet Jun 02 '21 at 14:47
  • @ScottSauyet An arrow function doesn't use its zeroth argument. Your example's purity depends on what `this` it closed over, and whether its `.discount` is constant. – Bergi Jun 02 '21 at 15:18
  • @Bergi, D'oh. But the same question arises if I use `function(a, b) {return (this?.discount ?? 1) * (a + b)}`. I guess I simply have a hard time with saying the purity of a function depends on a hidden parameter. I would still lean toward saying that these are impure. – Scott Sauyet Jun 02 '21 at 15:38
  • @ScottSauyet If you consider a closure than free variables are not that different really. But you wouldn't call a closure to be impure by design, right? It is only impure if it mutates the referenced value or reassigns the free variable itself. –  Jun 02 '21 at 16:53
  • @IvenMarquardt: I would consider a a function with closure formed by the partial application of an immutable variable to a curried function to be pure. But in a [closure over a mutable variable](http://link.fourwindssoft.com/24), even if the function in question didn't do the mutation, I would still not call it pure. It can give different results for the same input, which is the antithesis of purity. But with two very smart people trying to convince me otherwise, I know that I'm going to have to give this more thought. – Scott Sauyet Jun 02 '21 at 17:49
  • 2
    @ScottSauyet I do the same actually! everytime I run across such questions I give it more thought. I think we are all a bit right in what we say, because JS isn't referential transperent and thus there is no meaningful definition of purity anyway. I guess some people are just willing to be more confident that an expression in JS is pure, while others demand stronger guarantees. That's all. –  Jun 02 '21 at 19:01
  • 1
    @ScottSauyet Yeah, the hidden parameter might be confusing at first, but as soon as it's made explicit that we consider (late-bound) `this` as an input (function argument), I see no reason to treat it any different. And yes, I totally agree that a *closure* over a mutable variable (including arrow functions that use mutable properties of `this`) is not a pure function. `function example() { return this.x }` is pure (ignoring getters with side effects…), `example.bind(Object.freeze({x: 1}))` is pure, `example.bind(window)` is not. – Bergi Jun 02 '21 at 19:18
  • @Bergi: thanks for your patience. I still don't agree 100%, but with an immutable late-bound `this`, I can certainly agree with your point, modulo points like "ignoring getters with side effects". I've spent a lot of effort toward making JS a more hospitable environment for doing FP, but there's always a bit of square-peg/round-hole in doing so, and the number of ways that side-effects can happen is downright depressing. Thanks for you insight! And thank you too, Iven! – Scott Sauyet Jun 02 '21 at 20:01

1 Answers1

4

Should it be declared outside of the class to be considered as a pure function?

No, it doesn't matter. Purity (or: referential transparency) is a property of what the function (the code inside it) does, not where it is stored.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    I don't think this Q is as straightforward though, to be _pure_ a method should also be _side effects free_, however, it is kind of implicit for methods to depend on the instance context (`this`)... Probably, to be classified as _pure_ a method should also necessarily be `static` or the class be just a _namespace_. I would not classify `Foo#baz` as pure, even though, it honours the referential transparency principle. `class Foo { bar() {}; baz() { this.bar(); return true; }}` – Hitmands Jun 02 '21 at 20:00
  • @Hitmands I consider usage of the `this` argument to be unrelated from usage of side effects It is not closed-over state. (See also the discussion with Scott in the question comments). – Bergi Jun 02 '21 at 20:39