Status of side-effecting functions in python

2014-10-25 Thread Rustom Mody
Moved from other (Seymore's) thread where this is perhaps not relevant

On Saturday, October 25, 2014 1:15:09 PM UTC+5:30, Steven D'Aprano wrote:
> Rustom Mody wrote:
> 
> > On Saturday, October 25, 2014 11:20:03 AM UTC+5:30, Chris Angelico wrote:
> >> On Sat, Oct 25, 2014 at 4:40 PM, Rustom Mody  wrote:
> >> > Its generally accepted that side-effecting functions are not a good
> >> > idea -- typically a function that returns something and changes global
> >> > state.
> >> 
> >> Only in certain circles. Not in Python. There are large numbers of
> >> functions with side effects (mutator methods like list.append,
> >> anything that needs lots of state like random.random, everything with
> >> external effect like I/O, heaps of stuff), and it is most definitely
> >> not frowned upon.
> >> 
> >> In Python 3 (or Python 2 with the future directive), print is a
> >> function, print() an expression. It's not "semantically a statement".
> > 
> > Ok
> > So give me a valid (ie useful) use where instead of the usual
> > l=[1,2,3]
> > l.append(4)
> > 
> > we have
> > 
> > foo(l.append(4))
> 
> Your question seems to be non-sequitor. To me, it doesn't appear to have any
> relationship to Chris' comments.

| Languages like Pascal (many others)... distinguish function which return
| results and procedure which do not...
| Extract from https://en.wikipedia.org/wiki/Subroutine#Language_support

So my point: Whether the language supports it strongly (Pascal'
procedures) weakly (C's void functions) more weakly (Python's None
returning functions), the beginning programmer needs this concept as a core
thinking tool.

Pascal makes this easy -- teach the syntax and the concept will get across
Python is harder because the concept does not correlate with any specific syntax
But its not all that hard unless the teacher is stuck in correlating core 
concepts
and language syntax.

A teacher who is so stuck is cheating the student.

My version: 
"print may (3) or may not (2) be an expression. Just always consider it as a 
statement"

Chris version: print() is an expression. 
Technically Chris is correct. Is it methodologically/pedagogically it is sound?

Consider:

>From my explanation this:

>>> [print(x) for x in [1,2,3]]
1
2
3
[None, None, None]
>>> 

is in "Dont Do!" category whether python (3) allows it or python 2 doesn't.
And you "Dont do" because print(x) is a statement -- literally in
python 2; morally in python 3

And from here its only a small step to why l.append(4) should never
be used except as a statement. Python may not literally have Pascal's 
procedures;
they are morally there if you choose to 'believe' in them.

How would Chris inculcate avoidance of code-smells like
- foo(l.append(4))
- [print(x) for x in [1,2,3]]

  ?

No idea!

I dare say: about print I dont know but
[foo(x) for x in lst]
and foo is used for effect not return-value is
a rather common code-smell in my experience
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-25 Thread Wolfgang Maier

On 25.10.2014 19:27, Rustom Mody wrote:

Moved from other (Seymore's) thread where this is perhaps not relevant

On Saturday, October 25, 2014 1:15:09 PM UTC+5:30, Steven D'Aprano wrote:

Rustom Mody wrote:


On Saturday, October 25, 2014 11:20:03 AM UTC+5:30, Chris Angelico wrote:

On Sat, Oct 25, 2014 at 4:40 PM, Rustom Mody  wrote:

Its generally accepted that side-effecting functions are not a good
idea -- typically a function that returns something and changes global
state.


Only in certain circles. Not in Python. There are large numbers of
functions with side effects (mutator methods like list.append,
anything that needs lots of state like random.random, everything with
external effect like I/O, heaps of stuff), and it is most definitely
not frowned upon.

In Python 3 (or Python 2 with the future directive), print is a
function, print() an expression. It's not "semantically a statement".


Ok
So give me a valid (ie useful) use where instead of the usual
l=[1,2,3]
l.append(4)

we have

foo(l.append(4))


Your question seems to be non-sequitor. To me, it doesn't appear to have any
relationship to Chris' comments.


| Languages like Pascal (many others)... distinguish function which return
| results and procedure which do not...
| Extract from https://en.wikipedia.org/wiki/Subroutine#Language_support

So my point: Whether the language supports it strongly (Pascal'
procedures) weakly (C's void functions) more weakly (Python's None
returning functions), the beginning programmer needs this concept as a core
thinking tool.

Pascal makes this easy -- teach the syntax and the concept will get across
Python is harder because the concept does not correlate with any specific syntax
But its not all that hard unless the teacher is stuck in correlating core 
concepts
and language syntax.

A teacher who is so stuck is cheating the student.

My version:
"print may (3) or may not (2) be an expression. Just always consider it as a 
statement"

Chris version: print() is an expression.
Technically Chris is correct. Is it methodologically/pedagogically it is sound?

Consider:

 From my explanation this:


[print(x) for x in [1,2,3]]

1
2
3
[None, None, None]




is in "Dont Do!" category whether python (3) allows it or python 2 doesn't.
And you "Dont do" because print(x) is a statement -- literally in
python 2; morally in python 3

And from here its only a small step to why l.append(4) should never
be used except as a statement. Python may not literally have Pascal's 
procedures;
they are morally there if you choose to 'believe' in them.

How would Chris inculcate avoidance of code-smells like
- foo(l.append(4))
- [print(x) for x in [1,2,3]]



As Chris and Steven have pointed out, picking print() as an example does 
not make too much sense since it returns None.
It may be rare to use an expression both for its side-effects and its 
return value, but, provided you document the intention appropriately, I 
do not see what would generally be wrong with it.


A quick example that's not quite as silly as all your print() ones:

>>> with open('longnumber.txt', 'w') as out:
print(sum(out.write(str(x)) for x in range(100)), 'characters written.')


190 characters written.


--
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-25 Thread Dan Sommers
On Sat, 25 Oct 2014 23:41:52 +0200, Wolfgang Maier wrote:

> ... It may be rare to use an expression both for its side-effects and
> its return value ...

A lot of concurrency-related operations work that way.  In the old days,
it was CPU-level Test and Set (or Compare and Set) instructions.  These
days, we have Python's threading.Lock.acquire.

Dan
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-25 Thread Terry Reedy

On 10/25/2014 6:22 PM, Dan Sommers wrote:

On Sat, 25 Oct 2014 23:41:52 +0200, Wolfgang Maier wrote:


... It may be rare to use an expression both for its side-effects and
its return value ...


A lot of concurrency-related operations work that way.  In the old days,
it was CPU-level Test and Set (or Compare and Set) instructions.  These
days, we have Python's threading.Lock.acquire.


list.pop and and related set and dict methods.

--
Terry Jan Reedy

--
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-26 Thread Dan Sommers
On Sun, 26 Oct 2014 00:45:49 -0700, wxjmfauth wrote:

> Ditto for .write(). Why should it return "something" ?
> 
 with open('z.txt', 'w') as f:
> ... f.write('abc')
> ... 
> 3

OTOH, why shouldn't it return something?  In this case, it returns the
length of the string written.  This value is analogous to the value
returned by the underlying OS function (at least on a POSIX-like system,
where write(2) returns the number of bytes written).  This value can be
useful for detecting when things have gone wrong; e.g., disk full,
network down, pipe broken, etc.  Practicality definitely beats purity.

At one time, on a huge project, millions of lines of C and assembly
code, we had a local guideline *not* to write void functions.  The idea
was to return something that might be useful later, even if it seemed
unlikely now.  A simple success flag was sufficient; as functions grew,
often did their failure modes.

Dan
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-26 Thread Rustom Mody
On Sunday, October 26, 2014 7:11:43 PM UTC+5:30, Dan Sommers wrote:

> At one time, on a huge project, millions of lines of C and assembly
> code, we had a local guideline *not* to write void functions.  The idea
> was to return something that might be useful later, even if it seemed
> unlikely now.  A simple success flag was sufficient; as functions grew,
> often did their failure modes.

Well C and Python are completely different in this respect.
In C it is the norm to return the status in the return value
and the actual return value in pointer parameters.

Or even worse in pointer parameters + globals: think of most of the
system calls and errno.

This low-level messiness is in fact one of the compulsions that
have driven preference and development of higher level languages
like python.  Here failure-mode has dedicated syntax -- exceptions.  This 
conduces to a more DRY, less error-prone setup.

- Normal mode in the normal functional call-return style
- Failure mode in the except (for caller); raise (for callee) clauses.


Note that my comment was:

> Its generally accepted that side-effecting functions are not a good idea
> -- typically a function that returns something and changes global state. 

So I am not talking of merely global (or non-local) variable
side-effecting functions/methods which is normal in imperative
programming, but ones that do BOTH

- both changing global state
- returning useful results in the return value

While this may be called a code-smell, in C like languages it is
close to unavoidable.

In python its more avoidable and therefore more smelly (to my nose at least!)

Yeah I note Terry's examples of list.pop etc.
I guess one could say that these are instances of practicality beats purity.

However note the context where this thread arose.
A beginning programmer having a hard time distinguishing:

- Values and effects
- Expressions and statements
- Immutable and mutable data-structures
- Functions and Procedures

My claim is that until then, he should not be trying to write code like that.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-26 Thread Marko Rauhamaa
wxjmfa...@gmail.com:

> Yes and no. If something goes wrong in a .write() method,
> is not Python supposed to raise an error? (!)

We have multiple cases:

 1. write succeeds with all of the given bytes

 2. write succeeds with some but not all of the given bytes

 3. write cannot at the moment write any bytes

 4. an I/O error has taken place

Cases 1 and 2 are reported through positive return values in POSIX
system calls. Cases 3 and 4 are reported as errors. Arguably 2 and 3 are
related cases.

Python imitates POSIX, but *could* take a different tack. For example,

 * always return with an exception carrying the number of bytes written

 * return with None in case 1 and with an exception otherwise

 * return with the number of bytes in 1, 2 and 3 and exception in 4

The POSIX way is justified with symmetry: read() distinguishes EAGAIN
from 0 (end of file). One could argue that POSIX got it the wrong way.
EOF should be an exception ("EEOF") and 0 should be used instead of
EAGAIN. However, the POSIX practice works in C and Python.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-26 Thread Roy Smith
In article <683c84d8-d916-4b63-b4b2-92cd2763e...@googlegroups.com>,
 wxjmfa...@gmail.com wrote:

> Le dimanche 26 octobre 2014 14:41:43 UTC+1, Dan Sommers a écrit :
> > On Sun, 26 Oct 2014 00:45:49 -0700, wxjmfauth wrote:
> > 
> > > Ditto for .write(). Why should it return "something" ?
> > > 
> >  with open('z.txt', 'w') as f:
> > > ... f.write('abc')
> > > ... 
> > > 3
> > 
> > OTOH, why shouldn't it return something?  In this case, it returns the
> > length of the string written.  This value is analogous to the value
> > returned by the underlying OS function (at least on a POSIX-like system,
> > where write(2) returns the number of bytes written).  This value can be
> > useful for detecting when things have gone wrong; e.g., disk full,
> > network down, pipe broken, etc.  Practicality definitely beats purity.
> > 
> > At one time, on a huge project, millions of lines of C and assembly
> > code, we had a local guideline *not* to write void functions.  The idea
> > was to return something that might be useful later, even if it seemed
> > unlikely now.  A simple success flag was sufficient; as functions grew,
> > often did their failure modes.
> > 
> > Dan
> 
> Yes and no. If something goes wrong in a .write() method,
> is not Python supposed to raise an error? (!)

Define "wrong".  It is not an error for a write() call to consume fewer 
bytes than were requested.  How would you expect this to be handled in 
Python?  Raise DataPartiallyWrittenError?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-27 Thread Steven D'Aprano
Roy Smith wrote:

>> Yes and no. If something goes wrong in a .write() method,
>> is not Python supposed to raise an error? (!)
> 
> Define "wrong".  It is not an error for a write() call to consume fewer
> bytes than were requested.  

It's not? I'm asking a genuine question here, not a rhetorical one. I would
expect that if I ask to write 2 bytes, and only 1 byte is written, that
absolutely is an error. Under what circumstances is it okay for write() to
throw data away?

> How would you expect this to be handled in Python?  Raise
> DataPartiallyWrittenError? 

I would expect it to raise an IOError, most likely with one of the following
error codes:

* errno.EIO (physical input/output error)

* errno.EFBIG (file is too large)

* errno.ENOSPC (no space left on device, disk is full)


-- 
Steven

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-27 Thread Chris Angelico
On Mon, Oct 27, 2014 at 10:30 PM, Steven D'Aprano
 wrote:
> Roy Smith wrote:
>
>>> Yes and no. If something goes wrong in a .write() method,
>>> is not Python supposed to raise an error? (!)
>>
>> Define "wrong".  It is not an error for a write() call to consume fewer
>> bytes than were requested.
>
> It's not? I'm asking a genuine question here, not a rhetorical one. I would
> expect that if I ask to write 2 bytes, and only 1 byte is written, that
> absolutely is an error. Under what circumstances is it okay for write() to
> throw data away?
>
>> How would you expect this to be handled in Python?  Raise
>> DataPartiallyWrittenError?
>
> I would expect it to raise an IOError, most likely with one of the following
> error codes:
>
> * errno.EIO (physical input/output error)
>
> * errno.EFBIG (file is too large)
>
> * errno.ENOSPC (no space left on device, disk is full)

You're assuming the condition, whatever it is, is permanent. The most
common reason for write() to be temporarily unable to write everything
is a non-blocking socket, pipe, or somesuch. It writes as much as it
can, tells you how much that is, and lets you buffer the rest or deal
with it in whatever other way you choose.

If it is permanent, though, then yes, it should tell you. But what if
you ask it to write a megabyte, it writes half of it, and then finds
that there's no space on the disk? Should it:

1) Back out the write and raise an exception?
2) Write part of the data and raise an exception?
3) Write part of the data and NOT raise an exception?

All three make sense. The third one is an option only if it's
documented as being able to tell you about partial writes. The second
has a problem in that you can't necessarily communicate "this is how
much I wrote" properly while also signalling the exception (imagine if
one function calls write() more than once, and it doesn't catch any
exceptions, just lets them bubble up). And backing out a write isn't
always possible. So what's to do?

ChrisA
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-27 Thread Roy Smith
In article <544e2cf2$0$13009$c3e8da3$54964...@news.astraweb.com>,
 Steven D'Aprano  wrote:

> Roy Smith wrote:
> 
> >> Yes and no. If something goes wrong in a .write() method,
> >> is not Python supposed to raise an error? (!)
> > 
> > Define "wrong".  It is not an error for a write() call to consume fewer
> > bytes than were requested.  
> 
> It's not? I'm asking a genuine question here, not a rhetorical one. I would
> expect that if I ask to write 2 bytes, and only 1 byte is written, that
> absolutely is an error. Under what circumstances is it okay for write() to
> throw data away?

It's not throwing away data.  The write() call returns a count of how 
many bytes is consumed, so you can present the rest of them again in a 
later write() call (assuming that makes sense to do for your 
application).

In some cases, the underlying hardware (or network protocol) may be 
unable to handle the number of bytes you requested, or may fail in 
mid-transmission.  Imagine a serial link.  You tell it to write 100 
bytes.  It starts sending them down the line and after 20 bytes, the 
connection fails.  What should write() do in that case?  It hasn't 
written all the data, so it needs to let you know that.  It also has 
written *some* of the data, so it needs to let you know that too.  What 
you do with that information is up to you, but it clearly needs to 
return a richer status indication than just success/failure.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-27 Thread Grant Edwards
On 2014-10-25, Wolfgang Maier  wrote:

> It may be rare to use an expression both for its side-effects and its
> return value,

It's actually quite common.

For example:

   f = open("filename")
   d = f.readline()

In both of those lines, the side effects and return values are equally
vital.  The same applies to reading from a queue, popping from a
stack, etc.

-- 
Grant Edwards   grant.b.edwardsYow! This PORCUPINE knows
  at   his ZIPCODE ... And he has
  gmail.com"VISA"!!
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-27 Thread Grant Edwards
On 2014-10-27, Steven D'Aprano  wrote:
> Roy Smith wrote:
>
>>> Yes and no. If something goes wrong in a .write() method,
>>> is not Python supposed to raise an error? (!)
>> 
>> Define "wrong".  It is not an error for a write() call to consume fewer
>> bytes than were requested.  
>
> It's not? I'm asking a genuine question here, not a rhetorical one.

No.  

Under Unix/Posix a write() call may _always_ write fewer bytes than
requested.

It may be that the device/pipe/file whatever has filled and blocking
is disabled.  It may be that the device has decided that, at the
moment, it can only handle a certain amount of data for some other
reason.  For example: Let's say you're writing to a network connection
that must segment data, and you try to write more than will fit in the
current segment. The write() call may fill the segment, send the
segment, and "refuse" the rest of the data -- requiring that you make
a subsequent write() with the remaining data (at which point it will
start a new segment).

Or, it may be because the system call was interrupted by something
completely unrelated to your program, the write() call, or the "thing"
to which you're writing [and it was more convenient for whoever wrote
the OS to do a partial write than it was to try to resume the write].

If you really want to make sure that all bytes get written, you _must_
put all write() calls in a loop that checks the return value and keeps
re-writing any unwritten data.

And to answer your next question: yes, Unix application programmers
have been complaining about that (perhaps justifiably) since 1970.

-- 
Grant Edwards   grant.b.edwardsYow! I have a TINY BOWL in
  at   my HEAD
  gmail.com
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-27 Thread Marko Rauhamaa
Grant Edwards :

> If you really want to make sure that all bytes get written, you _must_
> put all write() calls in a loop that checks the return value and keeps
> re-writing any unwritten data.
>
> And to answer your next question: yes, Unix application programmers
> have been complaining about that (perhaps justifiably) since 1970.

I wouldn't have it any other way.

Now, I have confused the discussion with some misinformation myself.
Python2's file.write() doesn't return a value but pushes the whole
string out. Python3's file.write() returns the number of *characters*
written. I don't know if the number can ever be different from the total
number of characters in the string.

In POSIX, a write(2) system call on file blocks until all bytes have
been passed on to the file system. The only exception (no pun intended)
I know is the reception of a signal. Even then, I'm not sure Linux file
systems ever cut writes short because of signals. I think the lack of
nonblocking file access in Linux is one of the OS's main shortcomings.

Python's sockets and pipes don't have write methods.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-27 Thread Nobody
On Mon, 27 Oct 2014 17:14:58 +0200, Marko Rauhamaa wrote:

> In POSIX, a write(2) system call on file blocks until all bytes have been
> passed on to the file system. The only exception (no pun intended) I know
> is the reception of a signal.

Writing to a file (or block device) will return a short count in the event
that it results in the size of the file exceeding

* the space available on the partition,
* the user's quota,
* the process' file size limit (RLIMIT_FSIZE), or
* any implementation limit on the maximum size of a file,

and at least one byte can be written. This behaviour is mandated by POSIX.

This is different to writing to a socket, pipe or character device,
where a short count is considered an entirely normal result, and
a subsequent write for the remaining bytes will often succeed.

> Even then, I'm not sure Linux file systems ever cut writes short because
> of signals.

Linux never interrupts I/O on discs or block devices due to signals.
These are restarted regardless of whether the signal is set for
automatic restarting (SA_RESTART flag).

> I think the lack of nonblocking file access in Linux is one of the OS's
> main shortcomings. 

It doesn't really matter. In the absence of an explicit mlock() or
mlockall(), the actual code which would be controlling the access is
demand-paged from disc, as is the "memory" to/from which the data is
transferred (along with the memory which would hold the return code from
read() or write(), for that matter).

Asynchronous I/O in the sense of select(), poll(), O_NONBLOCK etc is meant
for situations where delays could be indefinite, e.g. network connections
or terminals. For "short" delays (i.e. disc access), there's not much
point having a mechanism so that you can avoid blocking while the data is
read from disc just so that you can block while the code in the "else"
branch is read from disc.

If you want the program to be able to do something else while waiting for
I/O, use threads. The introduction of threads made most concurrency-
related issues in the POSIX API moot.

-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-28 Thread Marko Rauhamaa
Nobody :

> Asynchronous I/O in the sense of select(), poll(), O_NONBLOCK etc is
> meant for situations where delays could be indefinite, e.g. network
> connections or terminals. For "short" delays (i.e. disc access),
> there's not much point having a mechanism so that you can avoid
> blocking while the data is read from disc just so that you can block
> while the code in the "else" branch is read from disc.

I disagree with the "shortness" of the delays.

> If you want the program to be able to do something else while waiting
> for I/O, use threads. The introduction of threads made most
> concurrency- related issues in the POSIX API moot.

I disagree about that point of view as well. If files played ball with
select() et al, the linux world would be a better, more coherent place.

For example, looking at Python3's asyncio, I don't want to go out of the
asyncio model just because of disk (or DB) access.


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: Status of side-effecting functions in python

2014-10-28 Thread Marko Rauhamaa
Marko Rauhamaa :

> Python's sockets and pipes don't have write methods.

Actually, that's mistaken as well. The sys.std* handles and pipes
returned by subprocess are accessed using file.write() and thus may
return partial writes.

That brings up another point: Python3's file.write() returns the number
of characters written. What might it return if a partial character
should be written?


Marko
-- 
https://mail.python.org/mailman/listinfo/python-list