Is there a way to change the closure of a python function?

Steve D'Aprano steve+python at pearwood.info
Wed Sep 28 23:53:46 EDT 2016


On Wed, 28 Sep 2016 07:18 pm, Chris Angelico wrote:

> On Wed, Sep 28, 2016 at 6:52 PM, Gregory Ewing
> <greg.ewing at canterbury.ac.nz> wrote:
>> Chris Angelico wrote:
>>>
>>>
>>> <greg.ewing at canterbury.ac.nz> wrote:
>>>
>>>> * No side effects (new variable bindings may be created, but
>>>>  existing ones cannot be changed; no mutable data structures).
>>>
>>>
>>> If that's adhered to 100%, the language is useless for any operation
>>> that cannot be handled as a "result at end of calculation" function.

They're useless for that too, because you, the caller, cannot see the result
of the calculation. Printing is a side-effect.

Technically you can view the program state through a debugger, but that's
exposing implementation details and besides its not very practical.

But really, this is nit-picking. It is a little like arguing that no
programming languages are actually Turing complete, since no language has
an infinite amount of memory available. Of course a programming language
needs to have *some* way of doing IO, be that print, writing to files, or
flicking the lights on and off in Morse Code, but the challenge is to wall
that off in such a way that it doesn't affect the desirable (to functional
programmers at least) "no side-effects" property.

However, there's no universally agreed upon definition of "side-effect".
Some people might disagree that printing to stdout is a side-effect in the
sense that matters, namely changes to *program* state. Changes to the rest
of the universe are fine.

Some will say that local state (local variables within a function) is okay
so long as that's just an implementation detail. Others insist that even
functions' internal implementation must be pure. After all, a function is
itself a program. If we believe that programs are easier to reason about if
they have no global mutable state (no global variables), then wrapping that
program in "define function"/"end function" tags shouldn't change that.

Others will point out that "no side-effects" is a leaky abstraction, and
like all such abstractions, it leaks. Your functional program will use
memory, the CPU will get warmer, etc.

http://www.johndcook.com/blog/2010/05/18/pure-functions-have-side-effects/

John Cook suggests that functional programming gets harder and harder to do
right (both for the compiler and for the programmer) as you asymptotically
approach 100% pure, and suggests the heuristic that (say) 85% pure is the
sweet spot: functional in the small, object oriented in the large.

http://www.johndcook.com/blog/2009/03/23/functional-in-the-small-oo-in-the-large/


I agree. I find that applying functional techniques to individual methods
makes my classes much better:

- local variables are fine;
- global variables are not;
- global constants are okay;
- mutating the state of the instance should be kept to the absolute minimum;
- and only done from a few mutator methods, not from arbitrary methods;
- attributes should *usually* be passed as arguments to methods, not 
  treated as part of the environment.


That last one is probably the most controversial. Let me explain.


Suppose I have a class with state and at least two methods:


class Robot:
    def __init__(self):
        self.position = (0, 0)
    def move(self, x, y):
        self.position[0] += x
        self.position[1] += y
    def report(self):
        # Robot, where are you?
        print("I'm at %r" % self.position)


So far so good. But what if we want to make the report() method a bit more
fancy? Maybe we want to spruce it up a bit, and allow subclasses to
customize how they actually report (print, show a dialog box, write to a
log file, whatever you like):

    def report(self):
        self.format()
        self.display_report() 

    def display_report(self):
        print("I'm at %r" % self.formatted_position)
       
    def format(self):
        self.formatted_position = (
                "x coordinate %f" % self.position[0],
                "y coordinate %f" % self.position[1]
                )

Now you have this weird dependency where format() communicates with report()
by side-effect, and you cannot test format() or display_report() except by
modifying the position of the Robot.

I see so much OO code written like this, and it is bad, evil, wrong, it
sucks and I don't like it! Just a little bit of functional technique makes
all the difference:

    def report(self):
        self.display_report(self.format(self.position)) 

    def display_report(self, report):
        print("I'm at %r" % report)
       
    def format(self, position):
        return ("x coordinate %f" % position[0],
                "y coordinate %f" % position[1]
                )

Its easier to understand them (less mystery state in the environment),
easier to test, and easier to convince yourself that the code is correct.



[...]
> If monads allow mutations or side effects, they are by definition not
> pure functions, and violate your bullet point. Languages like Haskell
> have them not because they are an intrinsic part of functional
> programming languages, but because they are an intrinsic part of
> practical/useful programming languages.

More about monads:

https://glyph.twistedmatrix.com/2016/02/microblog-607564DA-B525-4489-B337-CED9DDEDC099.html

And a little more serious:

http://stackoverflow.com/questions/3870088/a-monad-is-just-a-monoid-in-the-category-of-endofunctors-whats-the-issue

(despite the funny URL it is actually serious)


[...]
> Of course it's more than "a language that has functions"; but I'd say
> that a more useful comparison would be "languages that require
> functional idioms exclusively" vs "languages that support functional
> idioms" vs "languages with no functional programming support". Python
> is squarely in the second camp, with features like list
> comprehensions, map/reduce, etc, but never forcing you to use them.

List comps, map/reduce etc are the highest profile and least useful parts of
functional programming. The most useful is learning to avoid depending on
mutable state in your functions' environment, i.e. global variables,
instance attributes, etc. List comps etc are just a means to an end.




-- 
Steve
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list