On Wed, 28 Sep 2016 07:18 pm, Chris Angelico wrote: > On Wed, Sep 28, 2016 at 6:52 PM, Gregory Ewing > <greg.ew...@canterbury.ac.nz> wrote: >> Chris Angelico wrote: >>> >>> >>> <greg.ew...@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. -- https://mail.python.org/mailman/listinfo/python-list