1

I learnt that mutability of an object is defined with respect to its state but not with its identity.

Below program changes state of a locally scoped object referred by name count within function hailstone().

def hailstone(n):
    count = 1
    """Print the terms of the 'hailstone sequence' from n to 1."""
    assert n > 0
    print(n)
    if n > 1:
        if n % 2 == 0:
            count += hailstone(n / 2)
        else:
            count += hailstone((n * 3) + 1)
    return count

if (__name__ == '__main__'):
    result = hailstone(10)
    print(result)

After reading the below paragraph of this article, I see that the above code looks fine as regards changing the state of the locally scoped object that name count refers to (i.e. count += hailstone(n / 2)):

Ignore all that. Functional code is characterised by one thing: the absence of side effects. It doesn't rely on data outside the current function, and it doesn't change data that exists outside the current function. Every other “functional” thing can be derived from this property. Use it as a guide rope as you learn.

So, how do I understand the meaning of this statement from this answer:

In functional programming, it is not proper to ever change the value of a variable.

Does functional programming allow changing the state of the locally scoped object referred by name count in my above program?

Community
  • 1
  • 1
overexchange
  • 15,768
  • 30
  • 152
  • 347
  • Your code doesn't have side effects. There's no way of telling, from outside the function, whether it is implemented using `count` or not; it's a purely local variable. Also, integers in Python are immutable; the object referenced by `count` is never changed, the augmented assignment references a new object to the same name. – jonrsharpe Feb 05 '15 at 16:11
  • @jonrsharpe Yes, python creates new `int`type object whenever you modify the value(`>>> a = 2 >>> a += 2`) of that object. How would I define mutability? Is it wrt state change of an object or wrt identity change of an object? – overexchange Feb 06 '15 at 03:39
  • @overexchange see e.g. http://stackoverflow.com/q/8056130/3001761 – jonrsharpe Feb 06 '15 at 08:32
  • @jonrsharpe It is ideal to talk this topic language-agnostically unlike your given link. Despite, If I consider python, Yes, 1) The value of the object changes, but it changes by changing what the name refers to. This is immutable type object. `int` type object works like this in python. 2) Mutable type object has to change it's value "in place". I learnt that `function` type object is mutable in this sense, but I don't know how? any sample code? – overexchange Feb 06 '15 at 09:10
  • @overexchange it's helpful to think of names rather than variables (see http://nedbatchelder.com/text/names.html). One way to change a function in-place is to assign a new attribute to an existing function (e.g. `hailstone.foo = 'bar'`) – jonrsharpe Feb 06 '15 at 09:13
  • @jonrsharpe Yes, you are right!!! check my updated comment. How does it make sense to allow a `function` type object to add attribute with any `name` and bind any `object` to it? How it does not make sense to allow a `int` type object to add attribute with any `name` and bind any `object` to it? Any reference for `function` type object? – overexchange Feb 06 '15 at 09:20
  • 1
    @overexchange this is not an appropriate discussion; I'm not going to explain Python's whole object model to you in comments. Do some research! – jonrsharpe Feb 06 '15 at 09:23
  • @Matt It is not a strong statement to say that `scheme` or `haskell` does not allow re-assignments. It makes more sense to understand, whether I can use re-assignment in the above program. I feel, I should, Because `int` type objects are immutable in python. But the question is, Can we program in FP paradigm using re-assignment on local objects across any(FP/non-FP) language? – overexchange Feb 06 '15 at 09:43
  • @overexchange also as far as I know all pure FP languages implement tail recursion to optimize recursive functions calls but Python doesn't do this, so your code could fail for pretty big N with "RuntimeError: maximum recursion depth exceeded" – artemdevel Feb 06 '15 at 10:03
  • 2
    Related (on Programmers): http://programmers.stackexchange.com/q/196112/110531 – jonrsharpe Feb 06 '15 at 10:46
  • @jonrsharpe This is my [answer](http://codereview.stackexchange.com/a/80027/37254). – overexchange Feb 09 '15 at 12:25

4 Answers4

2

There is no clearcut definition of functional programming. However, it typically is meant to imply the absence, or at least the minimisation, of side effects -- sometimes people speak of pure functional programming to emphasise their complete absence. But what is a side effect?

A common definition is that a side effect is anything that breaks referential transparency. Referential transparency in turn can be defined in various ways, but perhaps the most enlightening definition is that order of evaluation should be irrelevant. That is, a program is referentially transparent (or pure) if you can simplify any of its subexpressions, in any order, merely by substituting definitions, without changing the outcome of the program. In particular, you can always replace a variable with its (unique!) definition.

Clearly, an assignments to a mutable variable breaks this condition, so it has to be considered a side effect -- even if it is merely local. Likewise a print statement, by the way.

There are ways to have mutable state, or IO, without breaking referential transparency. That is the mystical concept of a monad. But I won't go into that here.

Andreas Rossberg
  • 34,518
  • 3
  • 61
  • 72
0

Not knowing Python, I assume count is a "local" variable, not visible from outside, and every invokation of hailstone gets its own instance of count. If this is so, then, as @jonrsharpe explained, it is immaterial what you do to count.

Observe, however, that your code is needlessly verbose. Why not simply:

if n > 1:
    if n % 2 == 0:
        return 1 + hailstone(n/2)
    else:
        return 1 + hailstone(n*3+1)
else:
    return 1

It turns out that the variable is not needed at all, not to speak of the need to update it.

Ingo
  • 36,037
  • 5
  • 53
  • 100
0

Your function has no side effects (aside from the print) at the function level. That's a necessary, but not sufficient, condition to be called functional. Functional programs consist of one big expression. Your example is expressions at the function level, interspersed with imperative statements in the function body. An if without an else is a statement, as is a variable reassignment.

That's not to say that an imperative function without side effects isn't better than an imperative function with side effects. There are certainly advantages to your example, even if it isn't considered purely functional.

Karl Bielefeldt
  • 47,314
  • 10
  • 60
  • 94
0

This program mutates x:

x = 2
x = x * 3
x = x + 4
return x

In functional programming you don't use mutation. Your code should define each variable exactly once:

x1 = 2
x2 = x1 * 3
x3 = x2 + 4
return x3

It is possible that the compiler will allocate the same memory location for x1, x2 and x3, and mutate in place, as the original program does. However, this is an implementation detail and shouldn't be important.

Unlike the previous sequential program, you can think of the new one as a system of equations and read it in arbitrary order. In this case the computation still has to be done top-to-bottom due to data dependency, but this does not have to in general.

You can get rid of the "=" symbol altogether, by using functions. To get rid of "x1 = 2", parametrize the remaining lines by x1 and pass 2 to the function:

(function(x1) {
    x2 = x1 * 3
    x3 = x2 + 4
    return x3
})(2)

and continue this process to remove "x2 = x1 * 3" and "x3 = x2 * 4".

Sometimes you assign to a variable in a loop to accumulate something (e.g. add items in an array for x in A: sum += x). In this case, instead of repeated assignment you can rewrite the program to use functions such as "sum", "reduce", "map", "filter", which are high-level abstractions of loops.


I was lying. It is possible to have local mutable state in a pure functional program, as long as the mutability does not "leak" outside of the scope. In Haskell, there is a library called "ST" for this purpose. It embeds a small imperative language with commands 'create variable', 'read variable', 'write variable', and if your program does not leak a mutable variable, you can retrieve its result in a pure function. However, it is more awkward to use (see this answer) and not used as much as you use assignment in imperative languages.

Community
  • 1
  • 1
sdcvvc
  • 25,343
  • 4
  • 66
  • 102