Procedures and functions [was Re: Why not allow empty code blocks?]

Chris Angelico rosuav at gmail.com
Sun Jul 31 00:05:59 EDT 2016


On Sun, Jul 31, 2016 at 1:32 PM, Steven D'Aprano <steve at pearwood.info> wrote:
>> It means you can't
>> upgrade something from "always returns None" to "returns the number of
>> objects frobbed" without breaking compat.
>
> You shouldn't ever need to. At least that's the theory. In practice its not
> quite cut and dried. Procedures are for things which purely operate by
> side-effect: they're verbs, doing words:

That's all very well in theory, but as you say, it's far from cut and
dried. Taking the example that started all this off, Python 2 had
'print' as a statement (no return value), but the write method of I/O
streams (eg sys.stdout.write) has a definite and important return
value (the amount written).

As another example, the 'gc' module allows you to trigger garbage
collection immediately. (All examples from CPython. I don't know how
other Pythons handle things, but the specifics don't matter anyway.)
That's very definitely a verb - "go and take out the trash, now" - but
it's a function that returns the number of unreachable objects. More
or less the same concept as write(), in that it does some work and
then returns how much work it did.

So while I agree with Python's decision to make list.sort() return
None rather than the mutated list, I think there's not a dichotomy
here but a spectrum, from "procedure-like" to "function-like". At the
one extreme are control flow statements - it doesn't make any sense
whatsoever to ask about the return value of a "while" statement.
(Though you could define it as the value of the condition expression,
but that's something you want _inside_ the while block, not outside.)
Then you have things that Python has as statements, but which
plausibly could have return values, eg "def" and "class" could return
the function/class. Somewhere toward the middle are gc.collect() and
friends, where you mainly call them for their functionality, not their
return values. Leaning toward functional are the
mutators-with-return-values like list.pop() - there are times when you
don't care about the element, you just want to pop and discard. And
then the most functional of functions would be pure functions like
math.sin(), where they have no side effects whatsoever, just a return
value.

Which ones would you define as procedures and which as functions? Or
can you just define _everything_ as a function, and give them valid,
if meaningless, return values? Yeah.

ChrisA



More information about the Python-list mailing list