Re: generator/coroutine terminology

2015-04-18 Thread Albert van der Horst
In article 551e2cfd$0$11123$c3e8...@news.astraweb.com,
Steven D'Aprano  steve+comp.lang.pyt...@pearwood.info wrote:
On Wednesday 01 April 2015 00:18, Albert van der Horst wrote:

 In article 55062bda$0$12998$c3e8da3$54964...@news.astraweb.com,
 Steven D'Aprano  steve+comp.lang.pyt...@pearwood.info wrote:

The biggest difference is syntactic. Here's an iterator which returns a
never-ending sequence of squared numbers 1, 4, 9, 16, ...

class Squares:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
return self.i**2
def __iter__(self):
return self

 You should give an example of usage. As a newby I'm not up to
 figuring out the specification from source for
 something built of the mysterious __ internal
 thingies.
 (I did experiment with Squares interactively. But I didn't get
 further than creating a Squares object.)


Ah, sorry about that!

Usage is:

it = Squares()  # create an iterator
print(next(it))  # print the first value
x = next(it)  # extract the second
while x  100:
print(x)
x = next(it)


Beware of doing this:

for x in Squares():
print(x)

since Squares is an *infinite* generator, it will continue for ever if you
let it. Fortunately you can hit Ctrl-C to interrupt the for loop at any
point.

In Python 2, you will need to rename __next__ to just next without the
double-leading-and-trailing underscores.


Here's the same thing written as a generator:

def squares():
i = 1
while True:
yield i**2
i += 1

And for this one:

it = squares()  # create the iterator
print(next(it))  # print the first value
x = next(it)  # extract the second
while x  100:
print(x)
x = next(it)


Usage is pretty much exactly the same.

Thanks, I get it now. next and yield are more or less
switching between coroutines.



--
Steve

-- 
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spearc.xs4all.nl =n http://home.hccnet.nl/a.w.m.van.der.horst

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


Re: generator/coroutine terminology

2015-04-18 Thread CHIN Dihedral
On Friday, March 13, 2015 at 5:36:35 PM UTC+8, Marko Rauhamaa wrote:
 Rustom Mody rustompm...@gmail.com:
 
  Nice demo of the same confusing terminology we are talking about.
 
 Why don't you just stick with the terminology of the language
 specification? I think your students are going to be more confused if
 you try to come up with something better.
 
  When I say expect generators to close I mean 'generator-instances'
  ie at runtime
 
  When you say expect both to close with return you are talking of
  program-texts ie the 'factory'
 
  [Or I am guilty of the same I am accusing python of]
 
 Your 'factory' is a:
 
 generator
 A function which returns an iterator.
 URL: https://docs.python.org/3/glossary.html
 
 Your 'generator-instance' is an:
 
 iterator
 An object representing a stream of data.
 URL: https://docs.python.org/3/glossary.html
 
 I don't think you should read much into what str(obj) returns. It's not
 much more than a random printable some CPython coder has cooked up in
 the heat of the moment.
 
 
 Marko

Well, the functional that returns a function instance which can be used
as an interator  is also a function. 

Well, some might use terms in generics programming as described in the book by 
Gangs of 4. 

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


Re: generator/coroutine terminology

2015-04-03 Thread Steven D'Aprano
On Wednesday 01 April 2015 00:18, Albert van der Horst wrote:

 In article 55062bda$0$12998$c3e8da3$54964...@news.astraweb.com,
 Steven D'Aprano  steve+comp.lang.pyt...@pearwood.info wrote:

The biggest difference is syntactic. Here's an iterator which returns a
never-ending sequence of squared numbers 1, 4, 9, 16, ...

class Squares:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
return self.i**2
def __iter__(self):
return self
 
 You should give an example of usage. As a newby I'm not up to
 figuring out the specification from source for
 something built of the mysterious __ internal
 thingies.
 (I did experiment with Squares interactively. But I didn't get
 further than creating a Squares object.)


Ah, sorry about that!

Usage is:

it = Squares()  # create an iterator
print(next(it))  # print the first value
x = next(it)  # extract the second
while x  100:
print(x)
x = next(it)


Beware of doing this:

for x in Squares():
print(x)

since Squares is an *infinite* generator, it will continue for ever if you 
let it. Fortunately you can hit Ctrl-C to interrupt the for loop at any 
point.

In Python 2, you will need to rename __next__ to just next without the 
double-leading-and-trailing underscores.


Here's the same thing written as a generator:

def squares():
i = 1
while True:
yield i**2
i += 1

And for this one:

it = squares()  # create the iterator
print(next(it))  # print the first value
x = next(it)  # extract the second
while x  100:
print(x)
x = next(it)


Usage is pretty much exactly the same.


-- 
Steve

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


Re: generator/coroutine terminology

2015-04-03 Thread Paul Rubin
alb...@spenarnc.xs4all.nl (Albert van der Horst) writes:
 You should give an example of usage. As a newby I'm not up to
 figuring out the specification from source for
 something built of the mysterious __ internal
 thingies.

In reality because of generator expressions, the yield statement, and
some useful built-in generators in the itertools module, you rarely
have to use those special methods.  

I'd advise reading through the itertools module documentation from
beginning to end, trying to understand what everything does.  Even if
some are not that useful, it will help convey the mode of thinking that
went into these features.

At a somewhat deeper level you might like the SICP book: 

  http://mitpress.mit.edu/sicp/

It's somewhat old now and it's about Scheme rather than Python, but it
builds up the relevant concepts quite nicely.

Regarding the squares example, consider this even simpler generator:

  def count(n):
   while True:
 yield n
 n += 1

so count(1) yields 1, 2, 3, 4 ...

This is a very useful generator but you don't need to write it since
it's included in the itertools module as itertools.count.  Its initial
value defaults to 0.  So the  squares generator can be written:

   def squares():
  return (i*i for i in itertools.count(0))

Now if you want all the squares less than 100 (i.e. 0, 1, 4, 9, ..., 81):

  wanted = itertools.takewhile(lambda x: x100, squares())

You can print that out as a list:

   print(list(wanted))

The built-in sum function consumes an iterator, so you can add up the
squares less than 100:

  print(sum(itertools.takewhile(lambda x: x100, squares(

this prints 205 which is 1+4+9+16+25+36+49+64+81.

These features fit together quite elegantly and code like this flows off
the fingertips naturally once you've used to it.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-31 Thread Albert van der Horst
In article 83d579c1-ab61-4a3d-a834-e65d28eac...@googlegroups.com,
Rustom Mody  rustompm...@gmail.com wrote:
On Saturday, March 14, 2015 at 8:59:22 PM UTC+5:30, Rustom Mody wrote:
 On Saturday, March 14, 2015 at 11:34:27 AM UTC+5:30, Steven D'Aprano wrote:
 
  A generator (function) may be a function which returns an iterator,...

 I find generator-function misleading in the same way that pineapple
 misleadingly suggests apple that grows on pines

 A builtin function is a function in the builtin (or builtins -- can
never remember) module
 A pure function is function that does not assign or mutate non-locals
 A Steven-function is a function that presumably Steven wrote

 However a generator function is a weird sort of function (at best).
 Not regarding it as a function is IMO more reasonable.

Another analogy somewhat closer home than pineapples.
In Pascal there are procedures and functions.
Even in the venerable Fortran there are subroutine-subprogram and
(sub)function-subprogram.

The Algol 68 designers considered it a defect in the design.
They created a situation like in Python, where a def-thingy need
not return a value.


C took the stupid approach of just throwing out one of these.
A decade of troubles was enough to convince people that it was needed and the
mysterious 'void-returning' function was introduced to simulate procedures

The mistake this was intended to fix, was the rule that by default a
function returns int in C.

Groetjes Albert
-- 
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spearc.xs4all.nl =n http://home.hccnet.nl/a.w.m.van.der.horst

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


Re: generator/coroutine terminology

2015-03-31 Thread Albert van der Horst
In article mailman.372.1427809109.10327.python-l...@python.org,
Dave Angel  da...@davea.name wrote:
On 03/31/2015 09:18 AM, Albert van der Horst wrote:
 In article 55062bda$0$12998$c3e8da3$54964...@news.astraweb.com,
 Steven D'Aprano  steve+comp.lang.pyt...@pearwood.info wrote:


 The biggest difference is syntactic. Here's an iterator which returns a
 never-ending sequence of squared numbers 1, 4, 9, 16, ...

 class Squares:
 def __init__(self):
 self.i = 0
 def __next__(self):
 self.i += 1
 return self.i**2
 def __iter__(self):
 return self

 You should give an example of usage. As a newby I'm not up to
 figuring out the specification from source for
 something built of the mysterious __ internal
 thingies.
 (I did experiment with Squares interactively. But I didn't get
 further than creating a Squares object.)


He did say it was an iterator.  So for a first try, write a for loop:

class Squares:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
return self.i**2
def __iter__(self):
return self

for i in Squares():
 print(i)
 if i  50:
 break


This is what I get:
/ --
albert@cherry:/tmp$ more aap.py
class Squares:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
return self.i**2
def __iter__(self):
return self

albert@cherry:/tmp$ python
Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
[GCC 4.4.5] on linux2
Type help, copyright, credits or license for more information.
 from aap import *
 for i in Squares():
... print i
... if i50: break
...
Traceback (most recent call last):
  File stdin, line 1, in module
TypeError: instance has no next() method


/ --

Probably not what is intended.

Last minute note:
   renaming __next__() into next() did the job.

--
DaveA

Groetjes Albert
-- 
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spearc.xs4all.nl =n http://home.hccnet.nl/a.w.m.van.der.horst

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


Re: generator/coroutine terminology

2015-03-31 Thread Chris Angelico
On Wed, Apr 1, 2015 at 2:03 AM, Albert van der Horst
alb...@spenarnc.xs4all.nl wrote:
 class Squares:
 def __init__(self):
 self.i = 0
 def __next__(self):
 self.i += 1
 return self.i**2
 def __iter__(self):
 return self

 albert@cherry:/tmp$ python
 Python 2.6.6 (r266:84292, Dec 26 2010, 22:31:48)
 [GCC 4.4.5] on linux2
 Type help, copyright, credits or license for more information.
 from aap import *
 for i in Squares():
 ... print i
 ... if i50: break
 ...
 Traceback (most recent call last):
   File stdin, line 1, in module
 TypeError: instance has no next() method


 / --

 Probably not what is intended.

 Last minute note:
renaming __next__() into next() did the job.

That class was written for Python 3, not Python 2. In Py2, you need to
rename __next__ to next, as you noted, and you probably also want to
explicitly subclass object. Or just run it under Python 3. :)

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


Re: generator/coroutine terminology

2015-03-31 Thread Albert van der Horst
In article 55062bda$0$12998$c3e8da3$54964...@news.astraweb.com,
Steven D'Aprano  steve+comp.lang.pyt...@pearwood.info wrote:
Marko Rauhamaa wrote:

 Chris Angelico ros...@gmail.com:

 On Sun, Mar 15, 2015 at 9:15 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Is it necessary/useful for a Python application programmer to be
 conscious of the different types of iterator? What mistaken usage
 could arise if the application just treated all iterators as, well,
 iterators?

 If you treat them all as iterators, then you're safe, because that's
 the lowest common denominator. But there are a number of other
 iterators that have more features, including files, generators, etc.

 What features do generator iterators provide on top of generic
 iterators?

The biggest difference is syntactic. Here's an iterator which returns a
never-ending sequence of squared numbers 1, 4, 9, 16, ...

class Squares:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
return self.i**2
def __iter__(self):
return self

You should give an example of usage. As a newby I'm not up to
figuring out the specification from source for
something built of the mysterious __ internal
thingies.
(I did experiment with Squares interactively. But I didn't get
further than creating a Squares object.)




Here's the same thing written as a generator:

def squares():
i = 1
while True:
yield i**2
i += 1


Four lines, versus eight. The iterator version has a lot of boilerplate
(although some of it, the two-line __iter__ method, could be eliminated if
there was a standard Iterator builtin to inherit from).

Here's a more complicated example:

class Randoms:
def __init__(self):
self.finished = False
def __next__(self):
x = random.random()
if x  0.5:
self.finished = True
if self.finished:
raise StopIteration
else:
return x
def __iter__(self):
return self


def randoms():
x = random.random()
while x  0.5:
yield x
x = random.random()


Generators, as a rule, are significantly easier to write, understand, and
debug. There's nothing they can do that can't be done with an iterator
class, but the fiddly, unexciting bits related to halting and resuming and
saving state are all handled for you, allowing you to concentrate on the
behaviour you want, not the boilerplate.

This is illuminating. Thanks.


--
Steven

Groetjes Albert

-- 
Albert van der Horst, UTRECHT,THE NETHERLANDS
Economic growth -- being exponential -- ultimately falters.
albert@spearc.xs4all.nl =n http://home.hccnet.nl/a.w.m.van.der.horst

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


Re: generator/coroutine terminology

2015-03-31 Thread Dave Angel

On 03/31/2015 09:18 AM, Albert van der Horst wrote:

In article 55062bda$0$12998$c3e8da3$54964...@news.astraweb.com,
Steven D'Aprano  steve+comp.lang.pyt...@pearwood.info wrote:




The biggest difference is syntactic. Here's an iterator which returns a
never-ending sequence of squared numbers 1, 4, 9, 16, ...

class Squares:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
return self.i**2
def __iter__(self):
return self


You should give an example of usage. As a newby I'm not up to
figuring out the specification from source for
something built of the mysterious __ internal
thingies.
(I did experiment with Squares interactively. But I didn't get
further than creating a Squares object.)



He did say it was an iterator.  So for a first try, write a for loop:

class Squares:
   def __init__(self):
   self.i = 0
   def __next__(self):
   self.i += 1
   return self.i**2
   def __iter__(self):
   return self

for i in Squares():
print(i)
if i  50:
break

print(done)







Here's the same thing written as a generator:

def squares():
i = 1
while True:
yield i**2
i += 1


Four lines, versus eight. The iterator version has a lot of boilerplate
(although some of it, the two-line __iter__ method, could be eliminated if
there was a standard Iterator builtin to inherit from).


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


Re: generator/coroutine terminology

2015-03-16 Thread Mark Lawrence

On 17/03/2015 03:33, Rustom Mody wrote:

On Tuesday, March 17, 2015 at 8:55:27 AM UTC+5:30, Mark Lawrence wrote:

On 17/03/2015 03:18, Rustom Mody wrote:

On Tuesday, March 17, 2015 at 8:37:25 AM UTC+5:30, Mark Lawrence wrote:


Ok Let me throw out a suggestion:
- potato is a generator
- tomato is a cursor.
Acceptable?



No.  In Python potato is a generator function, tomato is a generator.
Why complicate something that is so simple?  I couldn't care less what
they are called in any other language.


Ok so lets see...
https://docs.python.org/3.4/tutorial/classes.html#generators
https://docs.python.org/3.4/glossary.html#term-generator

Are these consistent with (your notion of) python?
Maybe they are any other language?



I'll already suggested you write the patches and put them on the bug
tracker.  If you can't be bothered please have the courtesy to stop
bleating about it.


Here are two of your posts (within a couple of hours)

1.

So the docs are confused and inconsistent but in an open source
community it's not *MY* responsibility to deal with it, somebody else can.
Making mountains out of mole hills is all I see in this entire thread.


2.

No.  In Python potato is a generator function, tomato is a generator.
Why complicate something that is so simple?  I couldn't care less what
they are called in any other language.


I understand the first as saying: Since something is wrong correct it; Dont 
bleat!


The first is saying that *YOU* are complaining that the docs are 
inconsistent but *YOU* won't do anything about it.




The second is saying Nothing is wrong!



The second is saying that those are *MY* definitions that *I'M* 
perfectly happy with.



Please decide which side you belong



The side which hopes you give up this now very tedious thread.  Mario's 
response a few minutes back summed things up perfectly.


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: generator/coroutine terminology

2015-03-16 Thread Mark Lawrence

On 17/03/2015 02:52, Rustom Mody wrote:

On Monday, March 16, 2015 at 11:50:33 PM UTC+5:30, Mark Lawrence wrote:

On 16/03/2015 14:37, Rustom Mody wrote:

On Monday, March 16, 2015 at 7:57:08 PM UTC+5:30, Mark Lawrence wrote:

On 16/03/2015 14:19, Rustom Mody wrote:

==
Anyways...

Yes 15 years are past.
I dont expect the def can be revoked now.
[As far as I am concerned its a minor wart]
But the mess around the docs can certainly be cleaned up.



So write the patches to correct the docs, then raise the issue on the
bug tracker to get the patches accepted.  IIRC for doc patches you don't
even have to provide diff files against the rst files, plain text will
do, the core devs will do the rest for you.


I would gladly do that if it was a minor correction here and there.
But the problem is a bit deeper even though it can be kept mostly¹ in the docs
and not modify any syntax/semantics of python.

In particular for:

def potato(x):
yield x+1

tomato = potato(3)

what shall we call potato and tomato.
I believe this thread clearly shows that the docs are confused and inconsistent.
Yet I dont see any consensus on what/how to classify tomato/potato.

Function -- trouble on one side
Generator -- trouble on another
Iterator -- trouble on third
etc

¹ Grey areas excepted eg output of help(); inspect module etc



So the docs are confused and inconsistent but in an open source
community it's not *MY* responsibility to deal with it, somebody else can.

Making mountains out of mole hills is all I see in this entire thread.


Ok... Lets see... What if any agreement is there on this.

To start with basic terminology. Given


def potato(x):
yield x+1

tomato = potato(3)

What shall we call potato and tomato?


Steven suggested that potato can be called 'factory'
Not ideal, but way better than 'generator-function'.
Oscar does not like it.
No better/other suggestions (that we see here).

What next?

Ok Let me throw out a suggestion:
  - potato is a generator
  - tomato is a cursor.
Acceptable?



No.  In Python potato is a generator function, tomato is a generator. 
Why complicate something that is so simple?  I couldn't care less what 
they are called in any other language.


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: generator/coroutine terminology

2015-03-16 Thread Rustom Mody
On Tuesday, March 17, 2015 at 8:55:27 AM UTC+5:30, Mark Lawrence wrote:
 On 17/03/2015 03:18, Rustom Mody wrote:
  On Tuesday, March 17, 2015 at 8:37:25 AM UTC+5:30, Mark Lawrence wrote:
 
  Ok Let me throw out a suggestion:
 - potato is a generator
 - tomato is a cursor.
  Acceptable?
 
 
  No.  In Python potato is a generator function, tomato is a generator.
  Why complicate something that is so simple?  I couldn't care less what
  they are called in any other language.
 
  Ok so lets see...
  https://docs.python.org/3.4/tutorial/classes.html#generators
  https://docs.python.org/3.4/glossary.html#term-generator
 
  Are these consistent with (your notion of) python?
  Maybe they are any other language?
 
 
 I'll already suggested you write the patches and put them on the bug 
 tracker.  If you can't be bothered please have the courtesy to stop 
 bleating about it.

Here are two of your posts (within a couple of hours)

1. 
 So the docs are confused and inconsistent but in an open source
 community it's not *MY* responsibility to deal with it, somebody else can.
 Making mountains out of mole hills is all I see in this entire thread. 

2. 
 No.  In Python potato is a generator function, tomato is a generator.
 Why complicate something that is so simple?  I couldn't care less what
 they are called in any other language. 

I understand the first as saying: Since something is wrong correct it; Dont 
bleat!

The second is saying Nothing is wrong!

Please decide which side you belong
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Mario Figueiredo
On Mon, 16 Mar 2015 19:52:59 -0700 (PDT), Rustom Mody
rustompm...@gmail.com wrote:


When we go from 'simple-generators' to coroutine-generators there seem to be
bigger conceptual problems and implementation-gaffes.
Quite frankly I hardly understand this part.

There's a line after which terminology just becomes pedantic. And if
you cross it once more, you get into the realm of obfuscation.
Congratulations, I suppose! You managed to have reached that edge.

Seriously, you started this thread well. I even nodded to my screen
when many posts back you mentioned correct terminology was an
important aspect of any official spec.

But you just couldn't stop, could you? What you aren't realizing is
that your quest for the perfect set of terms is exactly what is
leading you into a confusing state, producing the opposite effect of
making things harder to understand, instead of easier.

Continue this thread if you must, but please never write a spec in
your life.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Mark Lawrence

On 17/03/2015 03:18, Rustom Mody wrote:

On Tuesday, March 17, 2015 at 8:37:25 AM UTC+5:30, Mark Lawrence wrote:


Ok Let me throw out a suggestion:
   - potato is a generator
   - tomato is a cursor.
Acceptable?



No.  In Python potato is a generator function, tomato is a generator.
Why complicate something that is so simple?  I couldn't care less what
they are called in any other language.


Ok so lets see...
https://docs.python.org/3.4/tutorial/classes.html#generators
https://docs.python.org/3.4/glossary.html#term-generator

Are these consistent with (your notion of) python?
Maybe they are any other language?



I'll already suggested you write the patches and put them on the bug 
tracker.  If you can't be bothered please have the courtesy to stop 
bleating about it.


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: generator/coroutine terminology

2015-03-16 Thread Rustom Mody
On Monday, March 16, 2015 at 11:50:33 PM UTC+5:30, Mark Lawrence wrote:
 On 16/03/2015 14:37, Rustom Mody wrote:
  On Monday, March 16, 2015 at 7:57:08 PM UTC+5:30, Mark Lawrence wrote:
  On 16/03/2015 14:19, Rustom Mody wrote:
  ==
  Anyways...
 
  Yes 15 years are past.
  I dont expect the def can be revoked now.
  [As far as I am concerned its a minor wart]
  But the mess around the docs can certainly be cleaned up.
 
 
  So write the patches to correct the docs, then raise the issue on the
  bug tracker to get the patches accepted.  IIRC for doc patches you don't
  even have to provide diff files against the rst files, plain text will
  do, the core devs will do the rest for you.
 
  I would gladly do that if it was a minor correction here and there.
  But the problem is a bit deeper even though it can be kept mostly¹ in the 
  docs
  and not modify any syntax/semantics of python.
 
  In particular for:
 
  def potato(x):
 yield x+1
 
  tomato = potato(3)
 
  what shall we call potato and tomato.
  I believe this thread clearly shows that the docs are confused and 
  inconsistent.
  Yet I dont see any consensus on what/how to classify tomato/potato.
 
  Function -- trouble on one side
  Generator -- trouble on another
  Iterator -- trouble on third
  etc
 
  ¹ Grey areas excepted eg output of help(); inspect module etc
 
 
 So the docs are confused and inconsistent but in an open source 
 community it's not *MY* responsibility to deal with it, somebody else can.
 
 Making mountains out of mole hills is all I see in this entire thread.

Ok... Lets see... What if any agreement is there on this.

To start with basic terminology. Given

 def potato(x):
yield x+1

 tomato = potato(3)

 What shall we call potato and tomato?

Steven suggested that potato can be called 'factory'
Not ideal, but way better than 'generator-function'.
Oscar does not like it.
No better/other suggestions (that we see here).

What next?

Ok Let me throw out a suggestion:
 - potato is a generator
 - tomato is a cursor.
Acceptable?

So much for terminological questions.
When we go from 'simple-generators' to coroutine-generators there seem to be
bigger conceptual problems and implementation-gaffes.
Quite frankly I hardly understand this part.
Here are some questions.

1. Is the name generator appropriate for coroutine-generator?
The wikipedia-link that Steven posted suggests the other-way-round 'is-a'
relation: a generator is a (semi)coroutine¹
2. Can we say
   yield-statement ≡ generator
   yield-expression ≡ coroutine
   ?
   What of x = yield y
   x = yield x

  My (vaguest) impression is that 'yield' stops being the meaningful word
  for coroutine -- see footnote from Frederik Lundh in the 2001 thread

 suspend instead of yield, but that's me

¹ Non-trivial terminological headache: Is this 'generator' in the old sense or 
new
sense?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Rustom Mody
On Tuesday, March 17, 2015 at 8:37:25 AM UTC+5:30, Mark Lawrence wrote:
 
  Ok Let me throw out a suggestion:
- potato is a generator
- tomato is a cursor.
  Acceptable?
 
 
 No.  In Python potato is a generator function, tomato is a generator. 
 Why complicate something that is so simple?  I couldn't care less what 
 they are called in any other language.

Ok so lets see...
https://docs.python.org/3.4/tutorial/classes.html#generators
https://docs.python.org/3.4/glossary.html#term-generator

Are these consistent with (your notion of) python?
Maybe they are any other language?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Steven D'Aprano
On Tue, 17 Mar 2015 12:13 am, Marko Rauhamaa wrote:

 If I get an iterator from a black box source, I don't know if I'm
 allowed/supposed to call close() on it.

In no particular order:

#1
if hasattr(some_iterator, 'close'):
some_iterator.close()


#2
close = getattr(some_iterator, 'close', None)
if close is not None:
close()


#3
try:
close = some_iterator.close
except AttributeError:
pass
else:
close()


#4 (The most pythonic solution of all.) 
If you want to call close(), for some reason, *make it part of your API* and
insist that whatever object is passed to you supports close(), or else it
is an error.




-- 
Steven

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


Re: generator/coroutine terminology

2015-03-16 Thread Marko Rauhamaa
Rustom Mody rustompm...@gmail.com:

 On Monday, March 16, 2015 at 6:02:48 PM UTC+5:30, Marko Rauhamaa wrote:
 So what we have now is:
   (1) plain iterators
   (2) generator iterators
   (3) coroutine generator iterators

 (1) and (2) are not unified, which I don't like.

 Can you expand on that a bit?

(2) contains methods, most notably close(), that (1) does not provide.
If I get an iterator from a black box source, I don't know if I'm
allowed/supposed to call close() on it.


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


Re: generator/coroutine terminology

2015-03-16 Thread Rustom Mody
On Monday, March 16, 2015 at 7:10:03 PM UTC+5:30, Steven D'Aprano wrote:
 And of course, from a comp science theoretic perspective,
 generators are a kind of subroutine, not a kind of type.

You just showed Marko a few posts back, that
A generator is a special case of an iterator.
And you wrote the iterator with 'class'.
And from python 2.2(?) classes are types.

So I dont know what comp science theoretic perspective you are describing...

From mine:

The generator
def gen():
  yield 1
  yield 2

is much closer to the list (ie data) [1,2]
than to say

def foo():
  print 1
  print 2

The only difference is that the list memoizes the data whereas the generator 
doesn't.

CLU perspective:
The iterator for a collection of complex_numbers can be used interchangeably 
with that for an array of integers
from https://en.wikipedia.org/wiki/CLU_%28programming_language%29
[Note: CLU-iterator ≡ python-generator]

Haskell perspective: 
Lists are by default lazy and memoized.
IOW in Haskell: List ≡ Lazy list ≡ python generator + memoization

Scheme perspective:
Programmer can choose between normal and lazy lists.
Lazy lists are just normal lists + delay/force where
delay x ≡ lambda: x
force x ≡ x()

==
Anyways...

Yes 15 years are past.
I dont expect the def can be revoked now.
[As far as I am concerned its a minor wart]
But the mess around the docs can certainly be cleaned up.

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


Re: generator/coroutine terminology

2015-03-16 Thread Steven D'Aprano
On Mon, 16 Mar 2015 11:51 pm, Rustom Mody wrote:

 It may help to read this 15 year old thread starting
 https://mail.python.org/pipermail/python-dev/2001-June/015478.html
 to see how many python stalwarts dont like the current state of affairs

/s/dont/didn't/

That's a fifteen year old thread, written before generators were introduced.
Guido's intuition has been vindicated -- generators have proven to be a
great and powerful feature, and the reuse of def for both generator
functions and regular functions has turned out to be no more confusing in
practice than the use of def for both functions and methods[1].


The argument is that generator-producers (def-with-yield) are not like
regular functions because calling the def-with-yield doesn't execute the
code inside them. Given three objects:

def f(): return 1

def g(): yield 1

class C: ...

the argument goes that:

* Calling f() executes the code inside f.

* Calling g() *doesn't* execute the code inside f, but runs 
  some initialisation code and returns an instance; it's the
  instance that executes the code inside f, using a special
  method.

* Calling C() also doesn't execute the code inside C, but 
  runs some initialisation code and returns an instance.


Therefore def-with-yield is closer to a class than a function and should not
reuse the same keyword. Or so the argument goes.

But this argument doesn't take into account the way generators are used.
They are used like functions, not like instances[2] like this:

# No
instance = gen()
instance.fe()
instance.fi()
instance.fo()
instance.fum()


but like functions, like this:

# Yes
for x in gen(): ...
sum(gen())
result = map(func, gen())


Here's a pure-Python version of map from Python 2:

def map(func, iterable):
 accum = []
 for item in iterable:
 accum.append(func(item))
 return accum

Here's the same thing for the Python 3 iterator version:

def map(func, iterable):
 for item in iterable:
 yield func(item)


The similarities when writing these are undeniable. Drop the accumulator,
replace any lines that store values into the accumulator with a yield, and
drop the final return. The way we write and use generators is closer to the
way we write and use functions than classes. If we rule out introducing a
new keyword, and insist on picking either def or class, I think it is
obvious that def is the better choice and class would be completely
inappropriate. And of course, from a comp science theoretic perspective,
generators are a kind of subroutine, not a kind of type.




[1] Methods are, of course, actually just functions under the hood. The
conversion to MethodType is done at method-lookup time, by the descriptor
protocol.

[2] I'm aware that they are instances. You know what I mean.


-- 
Steven

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


Re: generator/coroutine terminology

2015-03-16 Thread Chris Angelico
On Mon, Mar 16, 2015 at 10:51 PM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 Chris Angelico wrote:
 Just for the record, it's not just name bindings that make a generator
 behave as a coroutine. E.g.:

 py def co():
 ... print( (yield 1) + 2 )
 ...
 py a = co()
 py next(a)
 1
 py a.send(98)
 100
 Traceback (most recent call last):
   File stdin, line 1, in module
 StopIteration

Yep, I agree. It's not the name binding, it's whether the yield
expression's result is instantly discarded vs used in some way.

 It's surprising to me that you can send() into a non-coroutine and have it
 ignored. I'm not even sure that is documented behaviour or just an accident
 of implementation.

I agree conceptually; but there are two different consistencies here,
and they clash, my lords, they clash! One is that the API for a
coroutine includes send() but the API for a non-coroutine generator
doesn't, and that trying to send() into the latter should raise an
error. (Side point: What error? I'd accept AttributeError, but since
it's possible to mix sendable and nonsendable yields, it might be
better to have it raise at the time of the call - that is, when you
send a non-None value into a generator that's just going to discard
the value, it raises on arrival. In that case, possibly ValueError?
GeneratorStyleError?) And on the other hand, we have a very strong
Python convention that any expression can be used as a statement
without altering the effect of that expression in any way. Assigning
something to a name that you never use, or passing it as an argument
to a no-op function, should not suddenly change the behaviour of
anything.

 That said, though, it would be quite reasonable for a *linter* to warn
 you about sending into a generator that never uses sent values. With a
 good type inference system (not sure if MyPy is sufficient here), it
 would be possible to distinguish between those two functions and
 declare that the first one has the return type sendable_generator
 and the second one nonsendable_generator, and give a warning if you
 send into the latter. But I don't think that's the language's job.

 I do :-)

 I think it would be hard for a linter to do this. It can't just do a
 type-check, because the types of generator-iterators and
 generator-coroutines are the same. It would need to actually analyse the
 source code, AST, or byte-code. That's doable, but the compiler already
 does that, and compiles different code for the two cases:

 py from dis import dis
 py def g1():
 ... yield 1
 ...
 py def g2():
 ... x = yield 1
 ...
 py dis(g1)
   2   0 LOAD_CONST   1 (1)
   3 YIELD_VALUE
   4 POP_TOP
   5 LOAD_CONST   0 (None)
   8 RETURN_VALUE
 py dis(g2)
   2   0 LOAD_CONST   1 (1)
   3 YIELD_VALUE
   4 STORE_FAST   0 (x)
   7 LOAD_CONST   0 (None)
  10 RETURN_VALUE


 so it should be easy for the compiler to recognise a yield used as if it
 were a statement versus one used as an expression, and take appropriate
 steps. But maybe there are subtle corner cases I haven't thought of.

That code isn't actually all that different, though. What you have is this:

1) Load up a value
2) Yield, which pops the value off, yields it, and pushes the sent value or None
3a) in g1: Discard the value of the expression that we've just finished
3b) in g2: Store the value of that expression into x
4) Load None, and return it.

Steps 1, 3, and 4 would all be identical if you replace the yield 1
with, say, print(1). It'll pop the 1 off, do something, and leave a
result on the stack. The difference between assignment and
non-assignment comes later.

The risk I see here is that a simple optimization might suddenly make
a semantic change to something. Imagine this function:

def gen():
while True:
x = (yield 1)
if x == 5: break

Okay, so it cares about sent values. Well and good. Then we change it
so it doesn't always care:

def gen(state):
while True:
x = (yield 1)
if state and x == 5: break

Ahh but look! That's a mode-switch parameter. We should break that out
into two functions.

def gen_stoppable():
while True:
x = (yield 1)
if x == 5: break

def gen_unstoppable():
while True:
x = (yield 1)

Now, according to the current rules, you can change that last line to
just yield 1 without the two functions differing in API. There's no
sent value that will stop the second one, but it will accept and
ignore sent values, just as the first one does. (Imagine that's some
sort of password, and the second generator is for the case where the
account is locked and there IS no password.) But according to your
rules, changing it to no longer assign the yielded value somewhere
would make a semantic and API change to the function. That seems, to
me, at least as surprising as the case of 

Re: generator/coroutine terminology

2015-03-16 Thread Jonas Wielicki
On 16.03.2015 13:02, Steven D'Aprano wrote:
 (To be honest, I'm not even sure what the use-case for close() on coroutines
 is in the first place. If you don't want to send any more items into it,
 just don't send any more items into it.)

Just like with file-likes, it is useful to clean up resources. If you
use a coroutine with the lxml.etree.xmlfile interface [1] for instance
(code quoted from the reference):

def writer(out_stream):
with xmlfile(out_stream) as xf:
 with xf.element('{http://etherx.jabber.org/streams}stream'):
  try:
  while True:
  el = (yield)
  xf.write(el)
  xf.flush()
  except GeneratorExit:
  pass

This allows controlled (i.e. not governed by garbage collection) and
clean shutdown of a coroutine (albeit I’m not sure why they catch the
GeneratorExit here), which will close the opened stream tag.

regards,
jwi

   [1]: http://lxml.de/api.html#incremental-xml-generation






signature.asc
Description: OpenPGP digital signature
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Steven D'Aprano
Marko Rauhamaa wrote:

 Anyway, calling close() on an iterator can be a necessary requirement
 even in ordinary generator usage. Only now they have to know if the
 underlying iterator was sired by a generator or some other constructor.

Can you give an actual example of that?

(To be honest, I'm not even sure what the use-case for close() on coroutines
is in the first place. If you don't want to send any more items into it,
just don't send any more items into it.)


-- 
Steven

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


Re: generator/coroutine terminology

2015-03-16 Thread Marko Rauhamaa
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info:

 (To be honest, I'm not even sure what the use-case for close() on
 coroutines is in the first place. If you don't want to send any more
 items into it, just don't send any more items into it.)

Without close(), you might leave resources hanging. See PEP 325:

Rejected in favor of PEP 342 which includes substantially all of the
requested behavior in a more refined form.

And PEP 342:

Raymond Hettinger (PEP 288) and Samuele Pedroni (PEP 325) first
formally proposed the ideas of communicating values or exceptions
into generators, and the ability to close generators.

One *might* take the stance that resource allocation is outside the
scope of iterators, generator iterators and coroutine generator
iterators. As it stands, it is unclear if the application *must* call
close() on generator iterators that are left hanging.


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


Re: generator/coroutine terminology

2015-03-16 Thread Steven D'Aprano
Marko Rauhamaa wrote:

 Chris Angelico ros...@gmail.com:
 
 On Mon, Mar 16, 2015 at 6:12 PM, Marko Rauhamaa ma...@pacujo.net wrote:

 I was actually referring to the offered API. It still seems to me like
 all iterators could offer close(), send() and throw(). Why? To make all
 iterators drop-in replacements of each other.

 [...]

 That just adds unnecessary overhead to every iterator.
 
 Trivial implementations are a normal way to satisfy an API. That's why
 /dev/null implements a close() method, for example.

Absolutely. But you''ll notice it doesn't implement upper() and lower()
methods, because it isn't a string, it's a file.

Iterators shouldn't implement send() etc. methods because they aren't
coroutines.


 Also, what happens when you throw something into iter([1,2,3]) ? Or
 send it a value? What happens?
 
 I would expect iter([1, 2, 3]) to behave highly analogously to
 (x for x in [1, 2, 3]).

I believe that it is an unfortunate mistake that generators which aren't
coroutines support send. At the very least it is an ugly design wart, at
worst it is an outright bug.



-- 
Steven

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


Re: generator/coroutine terminology

2015-03-16 Thread Steven D'Aprano
Chris Angelico wrote:

 On Mon, Mar 16, 2015 at 7:36 PM, Steven D'Aprano
 steve+comp.lang.pyt...@pearwood.info wrote:
 I expect that the interpreter can tell the difference between

 yield spam

 and

 x = yield spam

 and only allow send(), close() and throw() on generators which include
 the second form. If it can't, then that's a limitation, not a feature to
 be emulated.

 
 Hmm. That would imply that an expression is different if it's used
 somewhere. In Python, there's no difference between these two function
 calls:
 
 x = func(1,2,3)
 func(1,2,3)
 
 except for the actual name binding.

That's a pretty big difference though :-)

Just for the record, it's not just name bindings that make a generator
behave as a coroutine. E.g.:

py def co():
... print( (yield 1) + 2 )
...
py a = co()
py next(a)
1
py a.send(98)
100
Traceback (most recent call last):
  File stdin, line 1, in module
StopIteration


Not the most *useful* example, I grant, but it demonstrates the idea.
However, for simplicity let's ignore that and pretend it's only name
binding we care about.


 If a yield expression enabled 
 send() if and only if its return value were used, then it would be
 extremely surprising:

It's surprising to me that you can send() into a non-coroutine and have it
ignored. I'm not even sure that is documented behaviour or just an accident
of implementation.

 
 def use_and_discard():
 while True:
 _ = (yield 1)
 
 def just_yield():
 while True:
 yield 1
 
 So... I can send into the first but not into the second?

That's the idea.

The second one is not a coroutine, and cannot be used as a coroutine in any
useful way. I maintain that it is a mistake that you can send() into it at
all, and if somebody does, it is invariably an error. That would be like
send()ing into None, or a str. We know what the Zen says about errors...


 That said, though, it would be quite reasonable for a *linter* to warn
 you about sending into a generator that never uses sent values. With a
 good type inference system (not sure if MyPy is sufficient here), it
 would be possible to distinguish between those two functions and
 declare that the first one has the return type sendable_generator
 and the second one nonsendable_generator, and give a warning if you
 send into the latter. But I don't think that's the language's job.

I do :-)

I think it would be hard for a linter to do this. It can't just do a
type-check, because the types of generator-iterators and
generator-coroutines are the same. It would need to actually analyse the
source code, AST, or byte-code. That's doable, but the compiler already
does that, and compiles different code for the two cases:

py from dis import dis
py def g1():
... yield 1
...
py def g2():
... x = yield 1
...
py dis(g1)
  2   0 LOAD_CONST   1 (1)
  3 YIELD_VALUE
  4 POP_TOP
  5 LOAD_CONST   0 (None)
  8 RETURN_VALUE
py dis(g2)
  2   0 LOAD_CONST   1 (1)
  3 YIELD_VALUE
  4 STORE_FAST   0 (x)
  7 LOAD_CONST   0 (None)
 10 RETURN_VALUE


so it should be easy for the compiler to recognise a yield used as if it
were a statement versus one used as an expression, and take appropriate
steps. But maybe there are subtle corner cases I haven't thought of.



-- 
Steven

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


Re: generator/coroutine terminology

2015-03-16 Thread Marko Rauhamaa
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info:

 If I .send() into either of the generators above, its a conceptual
 mistake and I should get an error. The fact that I don't is an
 implementation detail, and one which hopefully will be fixed.

So what we have now is:

  (1) plain iterators

  (2) generator iterators

  (3) coroutine generator iterators

At the moment (2) and (3) are unified, which you don't like. (1) and (2)
are not unified, which I don't like.

You and I can pretend (2) didn't have send(), throw() and close() and be
satisfied. Then, generator iterators are just plain iterators from the
point of view of the application programmer.

However, it seems to me close() is intended to be used for (2) as well.
The way I'm reading PEP 342, it seems to encompass the objectives of the
rejected PEP 325  (Resource-Release Support for Generators):


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


Re: generator/coroutine terminology

2015-03-16 Thread Rustom Mody
On Monday, March 16, 2015 at 6:02:48 PM UTC+5:30, Marko Rauhamaa wrote:
 So what we have now is:
 
   (1) plain iterators
 
   (2) generator iterators
 
   (3) coroutine generator iterators
 

 (1) and (2) are not unified, which I don't like.

Can you expand on that a bit?

It may help to read this 15 year old thread starting
https://mail.python.org/pipermail/python-dev/2001-June/015478.html
to see how many python stalwarts dont like the current state of affairs
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Marko Rauhamaa
Ian Kelly ian.g.ke...@gmail.com:

 Or inversely I write a normal utility function that is called from
 coroutines, then later add a yield from to it, and now I have to go
 back and revise every place where it's called to make those use yield
 from as well.

That is actually quite awkward. Smooth interlocking of coroutines
suggests you should be able to introduce a blocking state anywhere with
a yield from statement. To avoid the problem you mention, you'd then
have to give up direct function call altogether and yield from
everything under the sun:

 # Use the supercomputer over RPC -- or not!
 root = yield from my_math.sqrt(x)

It could be done, but it won't look like Python code anymore. It
probably won't look like a computer program anymore.


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


Re: generator/coroutine terminology

2015-03-16 Thread Rustom Mody
On Monday, March 16, 2015 at 8:07:22 PM UTC+5:30, Rustom Mody wrote:
 I would gladly do that if it was a minor correction here and there.
 But the problem is a bit deeper even though it can be kept mostly¹ in the docs
 and not modify any syntax/semantics of python.
 
 In particular for:
 
 def potato(x):
   yield x+1
 
 tomato = potato(3)
 
 what shall we call potato and tomato.
 I believe this thread clearly shows that the docs are confused and 
 inconsistent.
 Yet I dont see any consensus on what/how to classify tomato/potato.
 
 Function -- trouble on one side
 Generator -- trouble on another
 Iterator -- trouble on third 
 etc

And portmanteaus like generator-function are really the worst issue of all

I gave the example of 'pineapple'.

Steven gave another dozen examples that according to him are all ok.
Combine them with his earlier:
 Hopefully it is clear from context what we actually mean. When in doubt, we
 should be explicit. 

So if there is no ambiguity
pineapple can be apple (or is it pine)?
butterfly can be butter
And of course mush is much better than mushroom!

If anything the last 15 years have shown that portmanteaus will be shortformed;
they will be shortformed randomly and inconsistently;
even in the official docs and implementation -- not to mention all over the net.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Ian Kelly
On Mon, Mar 16, 2015 at 9:09 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Ian Kelly ian.g.ke...@gmail.com:

 For generators, the descriptive keyword (yield) could be buried
 *anywhere* in that block. One can glance at a generator function and
 fail to notice that it is a generator function. This is the one that
 really bugs me about reuse of def, although you are correct that
 this is a case where practicality has won over purity.

 I don't think that's all that big of a deal even though I will readily
 admit having stumbled on it early on. I removed the last yield
 statement from a generator expecting it to keep on being a generator.
 Thus, you have to do things like:

def nothing():
if False:
yield None

 The pass and global statements make me think it might be more
 Pythonic to have separate syntax for the special case:

def nothing():
yield not

My (limited) experience with asyncio is that it also makes this a bit
worse. I write a function intended to be a coroutine invoked from
coroutines using yield from, and then I realize that it's not a
coroutine because it never uses yield from itself. Or inversely I
write a normal utility function that is called from coroutines, then
later add a yield from to it, and now I have to go back and revise
every place where it's called to make those use yield from as well.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Steven D'Aprano
On Tue, 17 Mar 2015 01:35 am, Steven D'Aprano wrote:

 On Tue, 17 Mar 2015 01:19 am, Rustom Mody wrote:
 
 On Monday, March 16, 2015 at 7:10:03 PM UTC+5:30, Steven D'Aprano wrote:
 And of course, from a comp science theoretic perspective,
 generators are a kind of subroutine, not a kind of type.
 
 You just showed Marko a few posts back, that
 A generator is a special case of an iterator.
 And you wrote the iterator with 'class'.
 And from python 2.2(?) classes are types.
 
 Yes, but iterators aren't *classes*, they are instances.
 
 
 So I dont know what comp science theoretic perspective you are
 describing...
 
 http://en.wikipedia.org/wiki/Generator_(computer_programming)
 
 A generator is very similar to a function that returns an array, in that
 a generator has parameters, can be called, and generates a sequence of
 values. However, instead of building an array containing all the values
 and returning them all at once, a generator yields the values one at a
 time, which requires less memory and allows the caller to get started
 processing the first few values immediately. In short, a generator looks
 like a function but behaves like an iterator.

Oops, itchy trigger finger... the next paragraph is even more important:

Generators can be implemented in terms of more expressive control flow
constructs, such as coroutines or first-class continuations.[2] Generators,
also known as semicoroutines,[3] are a special case of (and weaker than)
coroutines, in that they always yield control back to the caller (when
passing a value back), rather than specifying a coroutine to jump to; see
comparison of coroutines with generators.



-- 
Steven

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


Re: generator/coroutine terminology

2015-03-16 Thread Steven D'Aprano
On Tue, 17 Mar 2015 01:19 am, Rustom Mody wrote:

 On Monday, March 16, 2015 at 7:10:03 PM UTC+5:30, Steven D'Aprano wrote:
 And of course, from a comp science theoretic perspective,
 generators are a kind of subroutine, not a kind of type.
 
 You just showed Marko a few posts back, that
 A generator is a special case of an iterator.
 And you wrote the iterator with 'class'.
 And from python 2.2(?) classes are types.

Yes, but iterators aren't *classes*, they are instances.


 So I dont know what comp science theoretic perspective you are
 describing...

http://en.wikipedia.org/wiki/Generator_(computer_programming)

A generator is very similar to a function that returns an array, in that a
generator has parameters, can be called, and generates a sequence of
values. However, instead of building an array containing all the values and
returning them all at once, a generator yields the values one at a time,
which requires less memory and allows the caller to get started processing
the first few values immediately. In short, a generator looks like a
function but behaves like an iterator.





-- 
Steven

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


Re: generator/coroutine terminology

2015-03-16 Thread Ian Kelly
On Mon, Mar 16, 2015 at 8:32 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 On Tue, 17 Mar 2015 12:13 am, Marko Rauhamaa wrote:

 If I get an iterator from a black box source, I don't know if I'm
 allowed/supposed to call close() on it.

 In no particular order:

 #1
 if hasattr(some_iterator, 'close'):
 some_iterator.close()


 #2
 close = getattr(some_iterator, 'close', None)
 if close is not None:
 close()


 #3
 try:
 close = some_iterator.close
 except AttributeError:
 pass
 else:
 close()

Note that these fail if some_iterator is a file-like object. Those
have close methods, but as part of the file api, not the iterator api,
and so the owner of the object is probably not expecting you to close
it for them.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Marko Rauhamaa
Ian Kelly ian.g.ke...@gmail.com:

 For generators, the descriptive keyword (yield) could be buried
 *anywhere* in that block. One can glance at a generator function and
 fail to notice that it is a generator function. This is the one that
 really bugs me about reuse of def, although you are correct that
 this is a case where practicality has won over purity.

I don't think that's all that big of a deal even though I will readily
admit having stumbled on it early on. I removed the last yield
statement from a generator expecting it to keep on being a generator.
Thus, you have to do things like:

   def nothing():
   if False:
   yield None

The pass and global statements make me think it might be more
Pythonic to have separate syntax for the special case:

   def nothing():
   yield not


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


Re: generator/coroutine terminology

2015-03-16 Thread Ian Kelly
On Mon, Mar 16, 2015 at 7:39 AM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 On Mon, 16 Mar 2015 11:51 pm, Rustom Mody wrote:

 It may help to read this 15 year old thread starting
 https://mail.python.org/pipermail/python-dev/2001-June/015478.html
 to see how many python stalwarts dont like the current state of affairs

 /s/dont/didn't/

 That's a fifteen year old thread, written before generators were introduced.
 Guido's intuition has been vindicated -- generators have proven to be a
 great and powerful feature, and the reuse of def for both generator
 functions and regular functions has turned out to be no more confusing in
 practice than the use of def for both functions and methods[1].


 The argument is that generator-producers (def-with-yield) are not like
 regular functions because calling the def-with-yield doesn't execute the
 code inside them. Given three objects:

There is another argument, which is that for functions and classes,
the descriptive keyword (def or class) is the first thing you see when
reading the code. For generators, the descriptive keyword (yield) could
be buried *anywhere* in that block. One can glance at a generator
function and fail to notice that it is a generator function. This is
the one that really bugs me about reuse of def, although you are
correct that this is a case where practicality has won over purity.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Mark Lawrence

On 16/03/2015 14:19, Rustom Mody wrote:

On Monday, March 16, 2015 at 7:10:03 PM UTC+5:30, Steven D'Aprano wrote:

And of course, from a comp science theoretic perspective,
generators are a kind of subroutine, not a kind of type.


You just showed Marko a few posts back, that
A generator is a special case of an iterator.
And you wrote the iterator with 'class'.
And from python 2.2(?) classes are types.

So I dont know what comp science theoretic perspective you are describing...

 From mine:

The generator
def gen():
   yield 1
   yield 2

is much closer to the list (ie data) [1,2]
than to say

def foo():
   print 1
   print 2

The only difference is that the list memoizes the data whereas the generator 
doesn't.

CLU perspective:
The iterator for a collection of complex_numbers can be used interchangeably 
with that for an array of integers
from https://en.wikipedia.org/wiki/CLU_%28programming_language%29
[Note: CLU-iterator ≡ python-generator]

Haskell perspective:
Lists are by default lazy and memoized.
IOW in Haskell: List ≡ Lazy list ≡ python generator + memoization

Scheme perspective:
Programmer can choose between normal and lazy lists.
Lazy lists are just normal lists + delay/force where
delay x ≡ lambda: x
force x ≡ x()

==
Anyways...

Yes 15 years are past.
I dont expect the def can be revoked now.
[As far as I am concerned its a minor wart]
But the mess around the docs can certainly be cleaned up.



So write the patches to correct the docs, then raise the issue on the 
bug tracker to get the patches accepted.  IIRC for doc patches you don't 
even have to provide diff files against the rst files, plain text will 
do, the core devs will do the rest for you.


--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: generator/coroutine terminology

2015-03-16 Thread Rustom Mody
On Monday, March 16, 2015 at 7:57:08 PM UTC+5:30, Mark Lawrence wrote:
 On 16/03/2015 14:19, Rustom Mody wrote:
  ==
  Anyways...
 
  Yes 15 years are past.
  I dont expect the def can be revoked now.
  [As far as I am concerned its a minor wart]
  But the mess around the docs can certainly be cleaned up.
 
 
 So write the patches to correct the docs, then raise the issue on the 
 bug tracker to get the patches accepted.  IIRC for doc patches you don't 
 even have to provide diff files against the rst files, plain text will 
 do, the core devs will do the rest for you.

I would gladly do that if it was a minor correction here and there.
But the problem is a bit deeper even though it can be kept mostly¹ in the docs
and not modify any syntax/semantics of python.

In particular for:

def potato(x):
  yield x+1

tomato = potato(3)

what shall we call potato and tomato.
I believe this thread clearly shows that the docs are confused and inconsistent.
Yet I dont see any consensus on what/how to classify tomato/potato.

Function -- trouble on one side
Generator -- trouble on another
Iterator -- trouble on third 
etc

¹ Grey areas excepted eg output of help(); inspect module etc
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Ian Kelly
On Mon, Mar 16, 2015 at 1:12 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 I was actually referring to the offered API. It still seems to me like
 all iterators could offer close(), send() and throw(). Why? To make all
 iterators drop-in replacements of each other.

The purpose of close, send and throw is to implement *coroutines*, not
iterators. The fact that coroutines in Python happen to be implemented
as a type of iterator doesn't mean that all iterators need to have
them. You're free to add these methods to non-generator iterators that
you write in order to create iterators that pretend to be coroutines,
but I'm having a hard time seeing what purpose would be served by
this.

If on the other hand these methods were to be added to the iterator
protocol, it would just create unnecessary implementation overhead for
the 99.99% of non-generator iterators that are under no illusions
about the fact that they aren't coroutines.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-16 Thread Marko Rauhamaa
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info:

 Marko Rauhamaa wrote:
 What features do generator iterators provide on top of generic
 iterators?

 The biggest difference is syntactic. Here's an iterator which returns a
 never-ending sequence of squared numbers 1, 4, 9, 16, ...

 class Squares:
 def __init__(self):
 self.i = 0
 def __next__(self):
 self.i += 1
 return self.i**2
 def __iter__(self):
 return self


 Here's the same thing written as a generator:

 def squares():
 i = 1
 while True:
 yield i**2
 i += 1

I was actually referring to the offered API. It still seems to me like
all iterators could offer close(), send() and throw(). Why? To make all
iterators drop-in replacements of each other.

Then, you could also stop wondering what to call the thingy returned by
a generator. Why, it would be an iterator. You wouldn't then have any
other use for a generator than the function that returns an iterator.

You could then decide if you want to reserve the name generator for
functions that contain a yield statement (syntactic notion) or call any
function returning an iterator a generator (semantic notion).


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


Re: generator/coroutine terminology

2015-03-16 Thread Chris Angelico
On Mon, Mar 16, 2015 at 6:12 PM, Marko Rauhamaa ma...@pacujo.net wrote:

 I was actually referring to the offered API. It still seems to me like
 all iterators could offer close(), send() and throw(). Why? To make all
 iterators drop-in replacements of each other.

 Then, you could also stop wondering what to call the thingy returned by
 a generator. Why, it would be an iterator. You wouldn't then have any
 other use for a generator than the function that returns an iterator.

That just adds unnecessary overhead to every iterator. Also, what
happens when you throw something into iter([1,2,3]) ? Or send it a
value? What happens?

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


Re: generator/coroutine terminology

2015-03-16 Thread Marko Rauhamaa
Chris Angelico ros...@gmail.com:

 On Mon, Mar 16, 2015 at 6:12 PM, Marko Rauhamaa ma...@pacujo.net wrote:

 I was actually referring to the offered API. It still seems to me like
 all iterators could offer close(), send() and throw(). Why? To make all
 iterators drop-in replacements of each other.

 [...]

 That just adds unnecessary overhead to every iterator.

Trivial implementations are a normal way to satisfy an API. That's why
/dev/null implements a close() method, for example.

 Also, what happens when you throw something into iter([1,2,3]) ? Or
 send it a value? What happens?

I would expect iter([1, 2, 3]) to behave highly analogously to
(x for x in [1, 2, 3]).

===begin /tmp/test.py===
#!/usr/bin/env python3

def main():
it = (x for x in [1, 2, 3])
print(next(it))
while True:
try:
print(it.send(hello))
except StopIteration:
break

if __name__ == __main__:
main()
===end /tmp/test.py=


$ python3 /tmp/test.py
1
2
3


but:

===begin /tmp/test.py===
#!/usr/bin/env python3

def main():
it = iter([1, 2, 3])
print(next(it))
while True:
try:
print(it.send(hello))
except StopIteration:
break

if __name__ == __main__:
main()
===end /tmp/test.py=


$ python3 /tmp/test.py
1
Traceback (most recent call last):
  File /tmp/test.py, line 13, in module
main()
  File /tmp/test.py, line 8, in main
print(it.send(hello))
AttributeError: 'list_iterator' object has no attribute 'send'



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


Re: generator/coroutine terminology

2015-03-16 Thread Marko Rauhamaa
Ian Kelly ian.g.ke...@gmail.com:

 If on the other hand these methods were to be added to the iterator
 protocol, it would just create unnecessary implementation overhead for
 the 99.99% of non-generator iterators that are under no illusions
 about the fact that they aren't coroutines.

Why should

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

be a valid coroutine but

   iter([1, 2, 3])

not?

Anyway, calling close() on an iterator can be a necessary requirement
even in ordinary generator usage. Only now they have to know if the
underlying iterator was sired by a generator or some other constructor.


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


Re: generator/coroutine terminology

2015-03-16 Thread Mark Lawrence

On 16/03/2015 14:37, Rustom Mody wrote:

On Monday, March 16, 2015 at 7:57:08 PM UTC+5:30, Mark Lawrence wrote:

On 16/03/2015 14:19, Rustom Mody wrote:

==
Anyways...

Yes 15 years are past.
I dont expect the def can be revoked now.
[As far as I am concerned its a minor wart]
But the mess around the docs can certainly be cleaned up.



So write the patches to correct the docs, then raise the issue on the
bug tracker to get the patches accepted.  IIRC for doc patches you don't
even have to provide diff files against the rst files, plain text will
do, the core devs will do the rest for you.


I would gladly do that if it was a minor correction here and there.
But the problem is a bit deeper even though it can be kept mostly¹ in the docs
and not modify any syntax/semantics of python.

In particular for:

def potato(x):
   yield x+1

tomato = potato(3)

what shall we call potato and tomato.
I believe this thread clearly shows that the docs are confused and inconsistent.
Yet I dont see any consensus on what/how to classify tomato/potato.

Function -- trouble on one side
Generator -- trouble on another
Iterator -- trouble on third
etc

¹ Grey areas excepted eg output of help(); inspect module etc



So the docs are confused and inconsistent but in an open source 
community it's not *MY* responsibility to deal with it, somebody else can.


Making mountains out of mole hills is all I see in this entire thread.

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: generator/coroutine terminology

2015-03-16 Thread Steven D'Aprano
On Monday 16 March 2015 18:12, Marko Rauhamaa wrote:

 Steven D'Aprano steve+comp.lang.pyt...@pearwood.info:
 
 Marko Rauhamaa wrote:
 What features do generator iterators provide on top of generic
 iterators?

 The biggest difference is syntactic. Here's an iterator which returns a
 never-ending sequence of squared numbers 1, 4, 9, 16, ...

 class Squares:
 def __init__(self):
 self.i = 0
 def __next__(self):
 self.i += 1
 return self.i**2
 def __iter__(self):
 return self


 Here's the same thing written as a generator:

 def squares():
 i = 1
 while True:
 yield i**2
 i += 1
 
 I was actually referring to the offered API. It still seems to me like
 all iterators could offer close(), send() and throw(). Why? To make all
 iterators drop-in replacements of each other.

How could these two iterators *possibly* be drop in replacements for each 
other in any real body of code?


def inorder(tree):
for node in inorder(tree.left):
yield node.payload
yield tree.payload
for node in inorder(tree.right):
yield node.payload


def clean_up(collection, condition, cleaner=None):
if cleaner is None:
def cleaner(obj):
try:
obj.cache = {}
obj.ref = None
except Exception:
return False
return True
for i, item in enumerate(collection):
if condition(item):
flag = cleaner(item)
if flag:
yield Warn(MSG % (i, item))


You can't just substitute any random iterator for another, any more than you 
can substitute any random two functions for each other.

Overly pure (in the comp sci theory sense) interfaces can be silly. Just 
because the Soyez rocket and my nephew's tricycle are both instances of 
Vehicle doesn't mean that I should be able to substitute one for the other. 
OOP theory says they should. Reality says No Way.

Why should I, the class designer, be forced to offer send(), throw() and 
close() methods on my iterators if I'm never going to use them as 
coroutines? If I .send() into either of the generators above, its a 
conceptual mistake and I should get an error. The fact that I don't is an 
implementation detail, and one which hopefully will be fixed. I expect that 
the interpreter can tell the difference between 

yield spam

and 

x = yield spam

and only allow send(), close() and throw() on generators which include the 
second form. If it can't, then that's a limitation, not a feature to be 
emulated.


 Then, you could also stop wondering what to call the thingy returned by
 a generator. 

Apart from a few die-hards who are hoping for some sort of Académie 
Française to define the One True meaning of words, most of us stopped 
wondering what to call the thingy returned by a generator function years 
ago. It's a generator, just like introspection of the object tells you.


 Why, it would be an iterator. You wouldn't then have any
 other use for a generator than the function that returns an iterator.

Lots of things are iterators. Doesn't make them generators.


 You could then decide if you want to reserve the name generator for
 functions that contain a yield statement (syntactic notion) or call any
 function returning an iterator a generator (semantic notion).

Why would you want the second? Do you have special names for {all functions 
which return strings} and {all functions which return bools}?

Functions are normally described by their *form* or *purpose*, not their 
return result, e.g. inner function, public function, callback function, 
reentrant function, filter function.


-- 
Steve

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


Re: generator/coroutine terminology

2015-03-16 Thread Chris Angelico
On Mon, Mar 16, 2015 at 7:36 PM, Steven D'Aprano
steve+comp.lang.pyt...@pearwood.info wrote:
 I expect that the interpreter can tell the difference between

 yield spam

 and

 x = yield spam

 and only allow send(), close() and throw() on generators which include the
 second form. If it can't, then that's a limitation, not a feature to be
 emulated.


Hmm. That would imply that an expression is different if it's used
somewhere. In Python, there's no difference between these two function
calls:

x = func(1,2,3)
func(1,2,3)

except for the actual name binding. If a yield expression enabled
send() if and only if its return value were used, then it would be
extremely surprising:

def use_and_discard():
while True:
_ = (yield 1)

def just_yield():
while True:
yield 1

So... I can send into the first but not into the second?

That said, though, it would be quite reasonable for a *linter* to warn
you about sending into a generator that never uses sent values. With a
good type inference system (not sure if MyPy is sufficient here), it
would be possible to distinguish between those two functions and
declare that the first one has the return type sendable_generator
and the second one nonsendable_generator, and give a warning if you
send into the latter. But I don't think that's the language's job.

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


Re: generator/coroutine terminology

2015-03-15 Thread Steven D'Aprano
Rustom Mody wrote:

 On Saturday, March 14, 2015 at 11:34:27 AM UTC+5:30, Steven D'Aprano
 wrote:
 
 A generator (function) may be a function which returns an iterator,...
 
 I find generator-function misleading in the same way that pineapple
 misleadingly suggests apple that grows on pines

You would be wrong. There's nothing objectionable or misleading
about generator-function meaning a function which returns generators.
English is very flexible like that, and compound nouns can be used in many
different ways:

* a greenhouse is not a house that happens to be green;

* a mushroom is not a room where people mush;

* a butterfly is not a fly made of butter;

* a swimming pool is not a pool which swims;

* but a flying squirrel is a squirrel which (almost) flies.

Where a compound noun is made of two nouns, the important one can appear
either at the beginning or the end:

* a printer cartridge is a type of cartridge, not a type of printer;

* but an attorney general is a type of attorney, not a type of general.


So there is nothing unusual about generator-function being a type of
function.


 A builtin function is a function in the builtin (or builtins -- can never
 remember) module A pure function is function that does not assign or
 mutate non-locals A Steven-function is a function that presumably Steven
 wrote
 
 However a generator function is a weird sort of function (at best).
 Not regarding it as a function is IMO more reasonable.

But it is a function.

You can regard it as a kind of yoghurt, if you like, but it isn't one.


py def g():
... yield 1
...
py from inspect import isfunction
py isfunction(g)
True



-- 
Steven

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


Re: generator/coroutine terminology

2015-03-15 Thread Steven D'Aprano
Marko Rauhamaa wrote:

 Chris Angelico ros...@gmail.com:
 
 On Sun, Mar 15, 2015 at 9:15 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Is it necessary/useful for a Python application programmer to be
 conscious of the different types of iterator? What mistaken usage
 could arise if the application just treated all iterators as, well,
 iterators?

 If you treat them all as iterators, then you're safe, because that's
 the lowest common denominator. But there are a number of other
 iterators that have more features, including files, generators, etc.
 
 What features do generator iterators provide on top of generic
 iterators?

The biggest difference is syntactic. Here's an iterator which returns a
never-ending sequence of squared numbers 1, 4, 9, 16, ...

class Squares:
def __init__(self):
self.i = 0
def __next__(self):
self.i += 1
return self.i**2
def __iter__(self):
return self


Here's the same thing written as a generator:

def squares():
i = 1
while True:
yield i**2
i += 1


Four lines, versus eight. The iterator version has a lot of boilerplate
(although some of it, the two-line __iter__ method, could be eliminated if
there was a standard Iterator builtin to inherit from).

Here's a more complicated example:

class Randoms:
def __init__(self):
self.finished = False
def __next__(self):
x = random.random()
if x  0.5:
self.finished = True
if self.finished:
raise StopIteration
else:
return x
def __iter__(self):
return self


def randoms():
x = random.random()
while x  0.5:
yield x
x = random.random()


Generators, as a rule, are significantly easier to write, understand, and
debug. There's nothing they can do that can't be done with an iterator
class, but the fiddly, unexciting bits related to halting and resuming and
saving state are all handled for you, allowing you to concentrate on the
behaviour you want, not the boilerplate.



-- 
Steven

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


Re: generator/coroutine terminology

2015-03-14 Thread Mark Lawrence

On 14/03/2015 20:14, Ian Kelly wrote:


Now which should be considered definitive, the language reference or
the PEP? This question is not rhetorical; I don't know the answer.
Regardless of the answer though, the PEP at least illuminates the
design intent of the terminology.



The language reference should be the definitive guide.

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: generator/coroutine terminology

2015-03-14 Thread Marko Rauhamaa
Oscar Benjamin oscar.j.benja...@gmail.com:

 A generator is a special type of iterator that results from generator
 functions and generator expressions.

Is it necessary/useful for a Python application programmer to be
conscious of the different types of iterator? What mistaken usage could
arise if the application just treated all iterators as, well, iterators?


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


Re: generator/coroutine terminology

2015-03-14 Thread Oscar Benjamin
On 12 March 2015 at 16:52, Rustom Mody rustompm...@gmail.com wrote:

 On Thursday, March 12, 2015 at 9:58:07 PM UTC+5:30, Steven D'Aprano wrote:
 Rustom Mody wrote:

 
  Say I have a simple yielding function:
 
  def foo(x):
 yield x+1
 yield x+2
 
  And I have
 
  g = foo(2)
 
  If I look at type, g's type is 'generator' whereas foo is just plain-ol
  'function.'
 
  Whereas in informal usage we say foo is a generator.

 Hopefully it is clear from context what we actually mean. When in doubt, we
 should be explicit.

 There is a very important 'context' where both have to exist together -- 
 teaching beginners.

It is definitely important to draw the distinction between a generator
function and a generator in teaching. I would use distinct terminology
in any case. It's also important where possible to use the same
terminology as is used in the various different sources of
documentation that your students will encounter so inventing a new
term like generator factory is probably a bad idea.

 foo's are written to produce g's. g's come from foo-like.

 Better naming would help clarify -- your 'factory' is the best Ive seen so 
 far.

I dislike the term generator factory. To me it suggests something
like foo below:

def baz():
yield 4

def bar():
yield 6
yield 7

def foo(x):
if x  4:
return baz()
else:
return bar()

The existing terminology seems fine to me: A generator function is a
special kind of function containing a yield statement that always
returns a generator. A generator expression is a special kind of
expression that evaluates to a generator. A generator is a special
type of iterator that results from generator functions and generator
expressions.

 But the docs?!?! Hoo Boy My head spins trying to grok this
 https://docs.python.org/3/reference/expressions.html#generator-expressions
 And thats after being familiar with the origins of the idea in
 scheme/simula/CLU etc.

Perhaps the docs can be improved. I wouldn't recommend that particular
page to anyone who wasn't already familiar with the subject though.


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


Re: generator/coroutine terminology

2015-03-14 Thread Chris Angelico
On Sun, Mar 15, 2015 at 9:15 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Oscar Benjamin oscar.j.benja...@gmail.com:

 A generator is a special type of iterator that results from generator
 functions and generator expressions.

 Is it necessary/useful for a Python application programmer to be
 conscious of the different types of iterator? What mistaken usage could
 arise if the application just treated all iterators as, well, iterators?

If you treat them all as iterators, then you're safe, because that's
the lowest common denominator. But there are a number of other
iterators that have more features, including files, generators, etc.

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


Re: generator/coroutine terminology

2015-03-14 Thread Chris Angelico
On Sun, Mar 15, 2015 at 11:48 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Chris Angelico ros...@gmail.com:

 On Sun, Mar 15, 2015 at 11:15 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 What features do generator iterators provide on top of generic
 iterators?

 You can send values into them, throw exceptions into them, and close
 them (which is a special case of the latter).

 Hm. I wonder why the distinction was made. IOW, why is

iter([1, 2, 3])

 not equivalent with

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

 I can close the latter but not the former.

You don't need to close the former.

 After all,

yield from [1, 2, 3]

 works all right.

That's using it as an iterable, which is not quite the same as an
iterator. (Iterators are themselves iterable; iter(x) is x, for any
iterator.) What you're doing there is roughly equivalent to:

for x in [1, 2, 3]:
yield x

but with a bunch of other simplifications and guarantees for edge
cases and things. So it's going to call iter() on what you give it.

If you treat things as iterators, you can use all sorts of things. You
don't need to distinguish the different types. The distinction is
important if you want to do more than just iterate over something.

 def gather():
lst = []
try:
  while True:
lst.append((yield len(lst)))
except StopIteration:
  yield lst

 n = gather()
 next(n)
0
 n.send(Hello)
1
 n.send(World)
2
 n.throw(StopIteration)
['Hello', 'World']

You can't do that with a simple iterator. (Not that I'm saying this is
good design...)

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


Re: generator/coroutine terminology

2015-03-14 Thread Marko Rauhamaa
Chris Angelico ros...@gmail.com:

 On Sun, Mar 15, 2015 at 11:15 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 What features do generator iterators provide on top of generic
 iterators?

 You can send values into them, throw exceptions into them, and close
 them (which is a special case of the latter).

Hm. I wonder why the distinction was made. IOW, why is

   iter([1, 2, 3])

not equivalent with

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

I can close the latter but not the former.

After all,

   yield from [1, 2, 3]

works all right.


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


Re: generator/coroutine terminology

2015-03-14 Thread Chris Angelico
On Sun, Mar 15, 2015 at 11:15 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Chris Angelico ros...@gmail.com:

 On Sun, Mar 15, 2015 at 9:15 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Is it necessary/useful for a Python application programmer to be
 conscious of the different types of iterator? What mistaken usage
 could arise if the application just treated all iterators as, well,
 iterators?

 If you treat them all as iterators, then you're safe, because that's
 the lowest common denominator. But there are a number of other
 iterators that have more features, including files, generators, etc.

 What features do generator iterators provide on top of generic
 iterators?

You can send values into them, throw exceptions into them, and close
them (which is a special case of the latter).

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


Re: generator/coroutine terminology

2015-03-14 Thread Marko Rauhamaa
Chris Angelico ros...@gmail.com:

 On Sun, Mar 15, 2015 at 9:15 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Is it necessary/useful for a Python application programmer to be
 conscious of the different types of iterator? What mistaken usage
 could arise if the application just treated all iterators as, well,
 iterators?

 If you treat them all as iterators, then you're safe, because that's
 the lowest common denominator. But there are a number of other
 iterators that have more features, including files, generators, etc.

What features do generator iterators provide on top of generic
iterators?


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


Re: generator/coroutine terminology

2015-03-14 Thread Ian Kelly
On Sat, Mar 14, 2015 at 1:54 AM, Marko Rauhamaa ma...@pacujo.net wrote:
 Steven D'Aprano steve+comp.lang.pyt...@pearwood.info:

 Marko Rauhamaa wrote:

 Your 'factory' is a:

 generator
 A function which returns an iterator.
 URL: https://docs.python.org/3/glossary.html

 That glossary entry is misleading, or at least incomplete, and it
 fails to match the way generator is used by actual Python
 programmers.

 I am an actual Python programmer (I develop Python programs) and my
 definitive source for Python is the documentation. If there is an error
 in the documentation, I would very much like it to be corrected.

I think that Steven was basing his statement on the definition you
cited. I don't think that he actually went and looked up the
definition. If he had, he would have seen that you omitted 90% of it.
Here's the full entry:


A function which returns an iterator. It looks like a normal function
except that it contains yield statements for producing a series of
values usable in a for-loop or that can be retrieved one at a time
with the next() function. Each yield temporarily suspends processing,
remembering the location execution state (including local variables
and pending try-statements). When the generator resumes, it picks-up
where it left-off (in contrast to functions which start fresh on every
invocation).


 A generator (function) may be a function which returns an iterator,
 but not all functions that return iterators are generators, and in
 some ways returning an iterator is the *least* interesting part of
 what makes a generator a generator.

 What distinguishes a generator from a regular function is the use of
 `yield`. Any definition which fails to mention that fact is useless.

As can be seen above, the glossary definition *does* in fact discuss
the use of yield.

 The language reference had better use more precise language. It *is* the
 definitive source, after all.

Okay, but you cited the glossary entry, not the language reference.
The language reference says this [1]:

When a generator function is called, it returns an iterator known as
a generator.

Which I think is quite clear, although to be fair the same section
also uses generator alone to refer to the function in the preceding
paragraph. That usage however is justified by PEP 255 [2]:


When a generator function is called, the actual arguments are bound to
function-local formal argument names in the usual way, but no code in
the body of the function is executed.  Instead a generator-iterator
object is returned; this conforms to the iterator protocol, so in
particular can be used in for-loops in a natural way.  Note that when
the intent is clear from context, the unqualified name generator may
be used to refer either to a generator-function or a
generator-iterator.



Now which should be considered definitive, the language reference or
the PEP? This question is not rhetorical; I don't know the answer.
Regardless of the answer though, the PEP at least illuminates the
design intent of the terminology.


[1] https://docs.python.org/3/reference/expressions.html#yield-expressions

[2] https://www.python.org/dev/peps/pep-0255/
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-14 Thread Rustom Mody
On Sunday, March 15, 2015 at 1:45:37 AM UTC+5:30, Ian wrote:
 Now which should be considered definitive, the language reference or
 the PEP? This question is not rhetorical; I don't know the answer.
 Regardless of the answer though, the PEP at least illuminates the
 design intent of the terminology.
  
  
 [1] https://docs.python.org/3/reference/expressions.html#yield-expressions
  
 [2] https://www.python.org/dev/peps/pep-0255/

Thanks

That PEP deserves careful reading, particularly the end...

| BDFL Pronouncements 
| Issue:  Introduce another new keyword (say, gen or generator) in
| place of def, or otherwise alter the syntax, to distinguish
| generator-functions from non-generator functions.
|  
| Con:  In practice (how you think about them), generators *are*
| functions, but with the twist that they're resumable.  The mechanics of
| how they're set up is a comparatively minor technical issue, and
| introducing a new keyword would unhelpfully overemphasize the
| mechanics of how generators get started (a vital but tiny part of a
| generator's life).
|  
| Pro:  In reality (how you think about them), generator-functions are
| actually factory functions that produce generator-iterators as if by
| magic.  In this respect they're radically different from non-generator
| functions, acting more like a constructor than a function, so reusing
| def is at best confusing.  A yield statement buried in the body is
| not enough warning that the semantics are so different.
|  
| BDFL:  def it stays.  No argument on either side is totally
| convincing, so I have consulted my language designer's intuition.  It
| tells me that the syntax proposed in the PEP is exactly right - not too
| hot, not too cold.  But, like the Oracle at Delphi in Greek mythology,
| it doesn't tell me why, so I don't have a rebuttal for the arguments
| against the PEP syntax.  The best I can come up with (apart from
| agreeing with the rebuttals ... already made) is FUD.  If this had
| been part of the language from day one, I very much doubt it would have
| made Andrew Kuchling's Python Warts page.

So while I dont go to the extent of suggesting introducing a different keyword 
for
generators, the fact that the overloading of def is problematic and arbitrary
comes from the horse's (ie BDFL's) mouth.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-14 Thread Steven D'Aprano
Marko Rauhamaa wrote:

 Your 'factory' is a:
 
 generator
 A function which returns an iterator.
 URL: https://docs.python.org/3/glossary.html

That glossary entry is misleading, or at least incomplete, and it fails to
match the way generator is used by actual Python programmers.

Here is a function which returns an iterator. According to the docs, it's a
generator, but that's simply wrong. In no way, shape or form should it be
considered to be a generator:

def not_a_generator():
return iter([1, 2, 3])


A generator (function) may be a function which returns an iterator, but not
all functions that return iterators are generators, and in some ways
returning an iterator is the *least* interesting part of what makes a
generator a generator.

What distinguishes a generator from a regular function is the use of
`yield`. Any definition which fails to mention that fact is useless.

Generator is used to describe both functions with the `yield` statement,
and the return result of calling such functions. Where it is necessary to
distinguish the two, we call the function-with-yield a generator
function. This terminology comes straight from the PEP introducing
generators:

https://www.python.org/dev/peps/pep-0255/


Such functions-with-yield *are* functions (or methods):

py def gen():
... yield 1
...
py type(gen)
type 'function'


but they're a special kind of function, distinguished by a flag on the
__code__ object (also known as func_code in Python 2). The inspect module
has a function to check that flag, which looks like this:

def isgeneratorfunction(object):
return bool((isfunction(object) or ismethod(object)) and
object.func_code.co_flags  CO_GENERATOR)


The result of calling `gen` is an instance of the generator type, that is to
say, an instance of a type which considers its own name to be generator:

py x = gen()
py type(x)
type 'generator'

Although this type is built-in, it is not available in the builtins
namespace, but it is bound to the name GeneratorType in the types module.
The inspect module has a function for this too:

def isgenerator(object):
return isinstance(object, types.GeneratorType)


For brevity, I've deleted the docstring, but it is very informative to read
it. Run `import inspect; help(inspect.isgenerator)` for more details.


Like many English words, we have two meanings for generator:

(1) A function containing the `yield` keyword, or generator-function.

(2) The result of calling such a function, an instance of
types.GeneratorType. PEP 255 calls that a generator-iterator, but that
name doesn't appear to have caught on anywhere.

If people can cope with the difference between a TV program and a computer
program, they can cope with generator having two meanings, especially
since we have ways to disambiguate between the two when needed.

 
 Your 'generator-instance' is an:
 
 iterator
 An object representing a stream of data.
 URL: https://docs.python.org/3/glossary.html

Generator instances are iterators, but not all iterators are generator
instances. Generator instances are special: they are subroutines which can
be suspended and resumed, with multiple exit points (each yield is an exit
point). 

Generators are a subset of coroutines, which are a generalization of
generators. Coroutines have multiple entry points and exit points (each
yield is both an entry point and exit point). CPython uses the same
internal mechanism for both, and as far as I know, there is no programmatic
way to distinguish a coroutine from a generator. Or at least no obvious
way -- there's no `inspect.iscoroutine` function that I know of.


 I don't think you should read much into what str(obj) returns. It's not
 much more than a random printable some CPython coder has cooked up in
 the heat of the moment.

I think that is completely wrong. The repr() and str() of generator
instances is hardly random, it reflects the consensus of the PEP authors
and the Python core developers, in particular the BDFL Guido who approved
the PEP, that the type of object it is should be called generator.



-- 
Steven

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


Re: generator/coroutine terminology

2015-03-14 Thread Mark Lawrence

On 14/03/2015 07:54, Marko Rauhamaa wrote:

Steven D'Aprano steve+comp.lang.pyt...@pearwood.info:


Marko Rauhamaa wrote:


Your 'factory' is a:

 generator
 A function which returns an iterator.
 URL: https://docs.python.org/3/glossary.html


That glossary entry is misleading, or at least incomplete, and it
fails to match the way generator is used by actual Python
programmers.


I am an actual Python programmer (I develop Python programs) and my
definitive source for Python is the documentation. If there is an error
in the documentation, I would very much like it to be corrected.



Five minutes work for you (singular) on the bug tracker.

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: generator/coroutine terminology

2015-03-14 Thread Marko Rauhamaa
Steven D'Aprano steve+comp.lang.pyt...@pearwood.info:

 Marko Rauhamaa wrote:

 Your 'factory' is a:
 
 generator
 A function which returns an iterator.
 URL: https://docs.python.org/3/glossary.html

 That glossary entry is misleading, or at least incomplete, and it
 fails to match the way generator is used by actual Python
 programmers.

I am an actual Python programmer (I develop Python programs) and my
definitive source for Python is the documentation. If there is an error
in the documentation, I would very much like it to be corrected.

 A generator (function) may be a function which returns an iterator,
 but not all functions that return iterators are generators, and in
 some ways returning an iterator is the *least* interesting part of
 what makes a generator a generator.

 What distinguishes a generator from a regular function is the use of
 `yield`. Any definition which fails to mention that fact is useless.

The language reference had better use more precise language. It *is* the
definitive source, after all.

 Like many English words, we have two meanings for generator:

 (1) A function containing the `yield` keyword, or generator-function.

 (2) The result of calling such a function, an instance of
 types.GeneratorType. PEP 255 calls that a generator-iterator, but
 that name doesn't appear to have caught on anywhere.

 If people can cope with the difference between a TV program and a
 computer program, they can cope with generator having two meanings,
 especially since we have ways to disambiguate between the two when
 needed.

I think that is untenable when you talk about a programming language.

 I don't think you should read much into what str(obj) returns. It's
 not much more than a random printable some CPython coder has cooked
 up in the heat of the moment.

 I think that is completely wrong. The repr() and str() of generator
 instances is hardly random, it reflects the consensus of the PEP
 authors and the Python core developers, in particular the BDFL Guido
 who approved the PEP, that the type of object it is should be called
 generator.

Be that as it may,

   URL: https://docs.python.org/3/reference/

and

   URL: https://docs.python.org/3/library/

are the definitive sources for Python application programmers.

Or is there a better reference you would recommend?


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


Re: generator/coroutine terminology

2015-03-14 Thread Marko Rauhamaa
Mark Lawrence breamore...@yahoo.co.uk:

 On 14/03/2015 07:54, Marko Rauhamaa wrote:
 I am an actual Python programmer (I develop Python programs) and my
 definitive source for Python is the documentation. If there is an
 error in the documentation, I would very much like it to be
 corrected.

 Five minutes work for you (singular) on the bug tracker.

I haven't determined that there is an error. I couldn't because to me,
the documentation defines right and wrong.

The only sign of discord so far has been between the return value of
str() and the language of the documentation. The return values of str()
and repr() are not defined very precisely:

 object.__repr__(self)
Called by the repr() built-in function to compute the “official”
string representation of an object. If at all possible, this should
look like a valid Python expression that could be used to recreate
an object with the same value (given an appropriate environment). If
this is not possible, a string of the form ...some useful
description... should be returned. The return value must be a
string object. [...]

 object.__str__(self)
Called by str(object) and the built-in functions format() and
print() to compute the “informal” or nicely printable string
representation of an object. The return value must be a string
object.

Thus one shouldn't read too much into what str() and repr() return. In
particular, there is no requirement that two valid Python
implementations ought to return similar-looking strings.


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


Re: generator/coroutine terminology

2015-03-14 Thread Dave Angel

On 03/14/2015 12:51 PM, Chris Angelico wrote:

On Sun, Mar 15, 2015 at 3:33 AM, Rustom Mody rustompm...@gmail.com wrote:

As best as I can see python makes no distinction between such a foo and
the more usual function/methods that have no returns.
You can I can talk about these and distinguish them
Python has no clue about it.


But what support is actually needed? Look, I can write functions that
return values:

def square(x):
 return x*x

four = square(2)

and functions that don't:

def save_to_file(fn):
 with open(fn, w) as f:
 f.write(stuff)
 f.write(more_stuff)

save_to_file(/tmp/asdf)

Who cares that the second one actually returns None and then ignores
that? It's not returning anything, it's not using the function return
value. This is, in effect, a procedure.

How is Python worse off for doing things this way rather than having a
true function with no return value or procedure concept?



And in C (at least the 80x86 implementations that I used) returns a 
value from a void function as well.  The value is an undefined int 
value, whatever the AX (EAX) register happened to contain.  The check 
that you didn't use such a value is made at compile time.


Further, in the original C definition, if you didn't define a return 
value, it was assumed to be int. And if you didn't define the arguments, 
they were assumed to be right.  In the C calling convention, the 
caller was responsible for popping off the arguments, since the called 
function didn't necessarily have a clue how many there were.  Then 
later, for efficiency reasons, they came up with the Pascal calling 
convention, where the callee popped the arguments.  It perhaps was 
related to the fact that the 80x86 instruction set had the return N 
instruction which decremented the call stack pointer an additional 
amount, at the same time as doing the return.


I think the Python convention is exactly right for its other 
characteristics.  No declarations, no distinction.


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


Re: generator/coroutine terminology

2015-03-14 Thread Rustom Mody
On Saturday, March 14, 2015 at 10:22:51 PM UTC+5:30, Chris Angelico wrote:
 On Sun, Mar 15, 2015 at 3:33 AM, Rustom Mody wrote:
  As best as I can see python makes no distinction between such a foo and
  the more usual function/methods that have no returns.
  You can I can talk about these and distinguish them
  Python has no clue about it.
 
 But what support is actually needed? Look, I can write functions that
 return values:
 
 def square(x):
 return x*x
 
 four = square(2)
 
 and functions that don't:
 
 def save_to_file(fn):
 with open(fn, w) as f:
 f.write(stuff)
 f.write(more_stuff)
 
 save_to_file(/tmp/asdf)
 
 Who cares that the second one actually returns None and then ignores
 that? It's not returning anything, it's not using the function return
 value. This is, in effect, a procedure.
 
 How is Python worse off for doing things this way rather than having a
 true function with no return value or procedure concept?

Well...
We can talk about that... maybe in another thread...
My more pressing question is that when I try to read the explanations of
generators, my tongue gets stuck behind my ears. And I suspect the problem is 
with the terminology

My original question was quite genuine:
Thanks to Marko's collections from the docs and also Steven's
 That glossary entry is misleading, or at least incomplete, and it fails to
 match the way generator is used by actual Python programmers. 

I guess I can confirm that the docs are messy the concepts are confused
and we (teachers) have to do whatever we will with that state of affairs.

[I admit to even having (tried to) teach C++. But I would not try to repeat that
feat if I had a choice]
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-14 Thread Rustom Mody
On Saturday, March 14, 2015 at 9:45:10 PM UTC+5:30, Chris Angelico wrote:
 On Sun, Mar 15, 2015 at 2:59 AM, Rustom Mody wrote:
  Causing all sorts of unnecessary confusions:
  An int-function returns int and a char*-functions returns char*.
  Does a void-function return void??
  No a void function doesn't return anything!
  Ah So a void function does a longjmp?
 
  All of which is to say that in retrospect we need (at least in imperative 
  programming) procedures and functions.
 
  Best if the language supports them
 
 Python has a broad concept of functions/methods that return something
 interesting and functions/methods that always return None. (The
 distinction often corresponds to non-mutator and mutator methods, but
 that's just convention.)

With due respect Chris, you are confused:

Sure any effective *pythonista* (who writes useful python) will have this 
concept.

Python (as against pythonistas) has no such concept¹ as function that ALWAYS
returns None

Consider this foo

 def foo(x):
...  if x0: return x-1
... 
 foo(3)
2
 foo(-1)


As best as I can see python makes no distinction between such a foo and 
the more usual function/methods that have no returns.
You can I can talk about these and distinguish them
Python has no clue about it.



Leaving aside latest type-annotation proposal.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-14 Thread Chris Angelico
On Sun, Mar 15, 2015 at 3:33 AM, Rustom Mody rustompm...@gmail.com wrote:
 As best as I can see python makes no distinction between such a foo and
 the more usual function/methods that have no returns.
 You can I can talk about these and distinguish them
 Python has no clue about it.

But what support is actually needed? Look, I can write functions that
return values:

def square(x):
return x*x

four = square(2)

and functions that don't:

def save_to_file(fn):
with open(fn, w) as f:
f.write(stuff)
f.write(more_stuff)

save_to_file(/tmp/asdf)

Who cares that the second one actually returns None and then ignores
that? It's not returning anything, it's not using the function return
value. This is, in effect, a procedure.

How is Python worse off for doing things this way rather than having a
true function with no return value or procedure concept?

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


Re: generator/coroutine terminology

2015-03-14 Thread Mark Lawrence

On 14/03/2015 16:33, Rustom Mody wrote:

On Saturday, March 14, 2015 at 9:45:10 PM UTC+5:30, Chris Angelico wrote:

On Sun, Mar 15, 2015 at 2:59 AM, Rustom Mody wrote:

Causing all sorts of unnecessary confusions:
An int-function returns int and a char*-functions returns char*.
Does a void-function return void??
No a void function doesn't return anything!
Ah So a void function does a longjmp?

All of which is to say that in retrospect we need (at least in imperative 
programming) procedures and functions.

Best if the language supports them


Python has a broad concept of functions/methods that return something
interesting and functions/methods that always return None. (The
distinction often corresponds to non-mutator and mutator methods, but
that's just convention.)


With due respect Chris, you are confused:

Sure any effective *pythonista* (who writes useful python) will have this 
concept.

Python (as against pythonistas) has no such concept¹ as function that ALWAYS
returns None

Consider this foo


def foo(x):

...  if x0: return x-1
...

foo(3)

2

foo(-1)



As best as I can see python makes no distinction between such a foo and
the more usual function/methods that have no returns.
You can I can talk about these and distinguish them
Python has no clue about it.



Python *ALWAYS* returns None for any path through a function that 
doesn't specify a return value.  Taking your example.


 def foo(x):
... if x0: return x-1
...
 import dis
 dis.dis(foo)
  2   0 LOAD_FAST0 (x)
  3 LOAD_CONST   1 (0)
  6 COMPARE_OP   4 ()
  9 POP_JUMP_IF_FALSE   20
 12 LOAD_FAST0 (x)
 15 LOAD_CONST   2 (1)
 18 BINARY_SUBTRACT
 19 RETURN_VALUE
   20 LOAD_CONST   0 (None)
 23 RETURN_VALUE

--
My fellow Pythonistas, ask not what our language can do for you, ask
what you can do for our language.

Mark Lawrence

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


Re: generator/coroutine terminology

2015-03-14 Thread Rustom Mody
On Saturday, March 14, 2015 at 11:34:27 AM UTC+5:30, Steven D'Aprano wrote:
 
 A generator (function) may be a function which returns an iterator,...

I find generator-function misleading in the same way that pineapple 
misleadingly suggests apple that grows on pines

A builtin function is a function in the builtin (or builtins -- can never 
remember) module
A pure function is function that does not assign or mutate non-locals
A Steven-function is a function that presumably Steven wrote 

However a generator function is a weird sort of function (at best).
Not regarding it as a function is IMO more reasonable.
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-14 Thread Chris Angelico
On Sun, Mar 15, 2015 at 2:29 AM, Rustom Mody rustompm...@gmail.com wrote:
 However a generator function is a weird sort of function (at best).
 Not regarding it as a function is IMO more reasonable.

But it *is* a function. You call it, possibly with positional and/or
keyword arguments, and you get back a returned object. How is this
different from any other function? (Yes, by this definition a class
could be called an object function. But the line is already somewhat
blurry; if I've read my history correctly, int used to be a
function, but now it's a type. A class is really just a special type
of factory function, just as a generator function is.)

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


Re: generator/coroutine terminology

2015-03-14 Thread Rustom Mody
On Saturday, March 14, 2015 at 8:59:22 PM UTC+5:30, Rustom Mody wrote:
 On Saturday, March 14, 2015 at 11:34:27 AM UTC+5:30, Steven D'Aprano wrote:
  
  A generator (function) may be a function which returns an iterator,...
 
 I find generator-function misleading in the same way that pineapple 
 misleadingly suggests apple that grows on pines
 
 A builtin function is a function in the builtin (or builtins -- can never 
 remember) module
 A pure function is function that does not assign or mutate non-locals
 A Steven-function is a function that presumably Steven wrote 
 
 However a generator function is a weird sort of function (at best).
 Not regarding it as a function is IMO more reasonable.

Another analogy somewhat closer home than pineapples.
In Pascal there are procedures and functions.
Even in the venerable Fortran there are subroutine-subprogram and 
(sub)function-subprogram.

C took the stupid approach of just throwing out one of these.
A decade of troubles was enough to convince people that it was needed and the
mysterious 'void-returning' function was introduced to simulate procedures

Causing all sorts of unnecessary confusions:
An int-function returns int and a char*-functions returns char*.
Does a void-function return void??
No a void function doesn't return anything!
Ah So a void function does a longjmp?

All of which is to say that in retrospect we need (at least in imperative 
programming) procedures and functions.

Best if the language supports them

If not, then we need them all the more in the conceptual ontology we use to
build programs.

Analogously we need the generator 'design-pattern' as well as the function
design-pattern. Conflating one as a special case of the other is a recipe for
confusion
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-14 Thread Chris Angelico
On Sun, Mar 15, 2015 at 2:59 AM, Rustom Mody rustompm...@gmail.com wrote:
 Causing all sorts of unnecessary confusions:
 An int-function returns int and a char*-functions returns char*.
 Does a void-function return void??
 No a void function doesn't return anything!
 Ah So a void function does a longjmp?

 All of which is to say that in retrospect we need (at least in imperative 
 programming) procedures and functions.

 Best if the language supports them

Python has a broad concept of functions/methods that return something
interesting and functions/methods that always return None. (The
distinction often corresponds to non-mutator and mutator methods, but
that's just convention.)

Pike has void and non-void functions, but you can sometimes trick the
system into giving you the return value of a void function. (Spoiler:
It'll be the integer 0.)

HTTP has some responses which contain no body (eg 304 NOT MODIFIED) as
well as many which do.

Ditto many other languages and protocols.

In each case, there's no _real_ distinction between functions and
procedures (Pike follows C in having void functions, but they're
still functions), and I don't see any evidence indicating that this
has damaged the languages or protocols concerned - can you show
otherwise?

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


Re: generator/coroutine terminology

2015-03-13 Thread Rustom Mody
On Friday, March 13, 2015 at 1:53:50 PM UTC+5:30, Chris Angelico wrote:
 On Fri, Mar 13, 2015 at 4:28 PM, Rustom Mody  wrote:
  And even there I would expect generators to close with StopIteration
  Whereas I would expect coroutines to close (on close method) with 
  GeneratorExit
  [Ive not thought all this through properly so may be off the mark]
 
 I expect both of them to close with return.

Nice demo of the same confusing terminology we are talking about.

When I say expect generators to close I mean 'generator-instances' ie at 
runtime

When you say expect both to close with return you are talking of program-texts
ie the 'factory'

[Or I am guilty of the same I am accusing python of]
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-13 Thread Marko Rauhamaa
Rustom Mody rustompm...@gmail.com:

 Nice demo of the same confusing terminology we are talking about.

Why don't you just stick with the terminology of the language
specification? I think your students are going to be more confused if
you try to come up with something better.

 When I say expect generators to close I mean 'generator-instances'
 ie at runtime

 When you say expect both to close with return you are talking of
 program-texts ie the 'factory'

 [Or I am guilty of the same I am accusing python of]

Your 'factory' is a:

generator
A function which returns an iterator.
URL: https://docs.python.org/3/glossary.html

Your 'generator-instance' is an:

iterator
An object representing a stream of data.
URL: https://docs.python.org/3/glossary.html

I don't think you should read much into what str(obj) returns. It's not
much more than a random printable some CPython coder has cooked up in
the heat of the moment.


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


Re: generator/coroutine terminology

2015-03-13 Thread Chris Angelico
On Fri, Mar 13, 2015 at 4:28 PM, Rustom Mody rustompm...@gmail.com wrote:
 And even there I would expect generators to close with StopIteration
 Whereas I would expect coroutines to close (on close method) with 
 GeneratorExit
 [Ive not thought all this through properly so may be off the mark]

I expect both of them to close with return. You can throw
GeneratorExit into a generator, but that's an implementation detail
more than anything else (same as the throwing of SystemExist is, and
for the same reason). When any iterator is exhausted, next() will
raise StopIteration rather than returning something; if you're simply
iterating over a generator, you can treat StopIteration as an
implementation detail too:

def squares_up_to(top):
for n in range(top):
if n*n  top: return
yield n*n

for sq in squares_up_to(50):
print(sq)

You don't need to be concerned about all those little details of
resuming and stack frames and so on. You definitely don't need to
worry about strange interactions around a with block inside the
generator - you can confidently trust that everything will behave
correctly. As it happens, this notion of behaving correctly is
largely implemented using exceptions, but if it were implemented with
a bunch of special cases in the CPython source code, it wouldn't
matter.

Write idiomatic source code and don't worry about the details. Learn
how things work if you're curious, but it's seldom going to matter.

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


Re: generator/coroutine terminology

2015-03-13 Thread Chris Angelico
On Fri, Mar 13, 2015 at 8:12 PM, Rustom Mody rustompm...@gmail.com wrote:
 On Friday, March 13, 2015 at 1:53:50 PM UTC+5:30, Chris Angelico wrote:
 On Fri, Mar 13, 2015 at 4:28 PM, Rustom Mody  wrote:
  And even there I would expect generators to close with StopIteration
  Whereas I would expect coroutines to close (on close method) with 
  GeneratorExit
  [Ive not thought all this through properly so may be off the mark]

 I expect both of them to close with return.

 Nice demo of the same confusing terminology we are talking about.

 When I say expect generators to close I mean 'generator-instances' ie at 
 runtime

 When you say expect both to close with return you are talking of 
 program-texts
 ie the 'factory'

 [Or I am guilty of the same I am accusing python of]

Well, if you're going to discuss the specifics of terminology, we'd
best figure out what close means :)

You spoke of generators closing with StopIteration. Does that mean
that, once a generator closes, it raises StopIteration? That's the job
of the next() method, in effect. Or do you mean that raising
StopIteration will terminate the generator? That won't be true
forever, and shouldn't be relied on - just use return.

If you use the close() method, Python throws a GeneratorExit into your
generator. But unless you have something that catches that, you
shouldn't need to worry about it; all you need to know is that you
call close() and the function cleanly terminates. Like I said, there
are implementation details that don't usually matter.

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


generator/coroutine terminology

2015-03-12 Thread Rustom Mody
This is more a question about standard terminology/conventions than about 
semantics - of course assuming I understand :-)

Say I have a simple yielding function:

def foo(x):
   yield x+1
   yield x+2

And I have

g = foo(2)

If I look at type, g's type is 'generator' whereas foo is just plain-ol 
'function.'

Whereas in informal usage we say foo is a generator.

So the question:
What should we call foo and what should we call g?

Same applies when foo is a 'coroutine' ie
something having yield used in an rhs and used with '.send' from outside:
What to call foo and what to call foo(x)?
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-12 Thread Chris Angelico
On Fri, Mar 13, 2015 at 12:35 AM, Rustom Mody rustompm...@gmail.com wrote:
 If I look at type, g's type is 'generator' whereas foo is just plain-ol 
 'function.'

 Whereas in informal usage we say foo is a generator.

 So the question:
 What should we call foo and what should we call g?

g is a generator object; foo is a generator function - a function
which returns generator objects. Usually, calling both of them
generators isn't confusing, as there's not often any context in
which they're ambiguous.

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


Re: generator/coroutine terminology

2015-03-12 Thread breamoreboy
On Thursday, March 12, 2015 at 1:35:48 PM UTC, Rustom Mody wrote:
 This is more a question about standard terminology/conventions than about 
 semantics - of course assuming I understand :-)
 
 Say I have a simple yielding function:
 
 def foo(x):
yield x+1
yield x+2
 
 And I have
 
 g = foo(2)
 
 If I look at type, g's type is 'generator' whereas foo is just plain-ol 
 'function.'
 
 Whereas in informal usage we say foo is a generator.
 
 So the question:
 What should we call foo and what should we call g?
 
 Same applies when foo is a 'coroutine' ie
 something having yield used in an rhs and used with '.send' from outside:
 What to call foo and what to call foo(x)?

Try the glossary https://docs.python.org/3/glossary.html

If this comes out badly please free to shout as I'm on gg :)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-12 Thread Rustom Mody
On Friday, March 13, 2015 at 9:00:17 AM UTC+5:30, Steven D'Aprano wrote:
 Rustom Mody wrote:
 
  On Thursday, March 12, 2015 at 11:25:32 PM UTC+5:30, Marko Rauhamaa wrote:
  Rustom Mody :
  
   I guess we need
   1. A clear ontology of the base concepts (which is a buzzword for
   nailed-down terminology)
  
  According to the documentation, a function whose definition contains a
  yield statement is a generator:
  
 Using a yield expression in a function's body causes that function to
 be a generator.
  
 URL: https://docs.python.org/3/reference/expressions.html#yield-exp
 ressions
  
 generator
A function which returns an iterator.
  
 URL: https://docs.python.org/3/glossary.html#term-generator
  
  Apparently, what we have here is a discrepancy between the documentation
  and the CPython implementation.
  
  In a word, it's a bug (somewhere).
  
  Throwing out some thought for better terminology.
  [Yeah needs to be chewed on a bit...]
  
  With respect to my example above:
  
  def foo(x):
 yield x+1
 yield x+2
  
  
  g = foo(2)
  
  Generator-instance: g
  Generator-factory: foo
  Generator: Concept in python (ontology) elaborated in terms of the above
 
 I think that is is reasonable.

Good that we agree!

 Where the distinction is clear, or not important, than calling both g and
 foo generators is acceptable shorthand. I realise that can be confusing
 to beginners, but jargon often is confusing to beginners until they have
 learned enough to be able to correctly interpret things in context.

I would prefer -- when shortforming is required:
generator-instance - instance
generator-factory - factory

rather than

generator-instance - generator
generator-factory - generator

Yeah that can quarrel with the more usual OO notion of instance, but  I find 
that ambiguity more remote
 
 
  Situation with coroutines is worse
  [Or I cant think of good terms...]
 
 Not really. The difference between a coroutine and a generator is mostly in
 usage, that is, intent. They both use the same type, they both have send
 methods, they both are generated the same way. If you are foolish, you can
 even mix generator-behaviour and coroutine-behaviour in the same function:
 
 
 py def g():
 ... yield 1
 ... yield 2
 ... x = yield 3
 ... yield x + 1000
 ...
 py a = g()
 py next(a)
 1
 py next(a)
 2
 py next(a)
 3
 py a.send(99)
 1099
 
 But don't do that.
 
 So as far as Python is concerned, a coroutine is just a generator instance
 which you use in a particular way, not a different kind of object. So I
 would use similar terminology:
 
 A generator function/factory returns a generator instance, which we use as a
 coroutine, so we can call it a coroutine.

I think coroutines are intended to be symmetric in a way that generators are 
not.

The nut-n-bolts of both may involve using yield but conceptually I find them 
different.

And even there I would expect generators to close with StopIteration
Whereas I would expect coroutines to close (on close method) with GeneratorExit
[Ive not thought all this through properly so may be off the mark]
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-12 Thread Steven D'Aprano
Rustom Mody wrote:

 On Thursday, March 12, 2015 at 11:25:32 PM UTC+5:30, Marko Rauhamaa wrote:
 Rustom Mody :
 
  I guess we need
  1. A clear ontology of the base concepts (which is a buzzword for
  nailed-down terminology)
 
 According to the documentation, a function whose definition contains a
 yield statement is a generator:
 
Using a yield expression in a function's body causes that function to
be a generator.
 
URL: https://docs.python.org/3/reference/expressions.html#yield-exp
ressions
 
generator
   A function which returns an iterator.
 
URL: https://docs.python.org/3/glossary.html#term-generator
 
 Apparently, what we have here is a discrepancy between the documentation
 and the CPython implementation.
 
 In a word, it's a bug (somewhere).
 
 Throwing out some thought for better terminology.
 [Yeah needs to be chewed on a bit...]
 
 With respect to my example above:
 
 def foo(x):
yield x+1
yield x+2
 
 
 g = foo(2)
 
 Generator-instance: g
 Generator-factory: foo
 Generator: Concept in python (ontology) elaborated in terms of the above

I think that is is reasonable. I would include generator function as a
synonym for generator factory. (Factories are more inclusive than
functions -- a factory could include a class with a make_generator
method, for example.)

Where the distinction is clear, or not important, than calling both g and
foo generators is acceptable shorthand. I realise that can be confusing
to beginners, but jargon often is confusing to beginners until they have
learned enough to be able to correctly interpret things in context.


 Situation with coroutines is worse
 [Or I cant think of good terms...]

Not really. The difference between a coroutine and a generator is mostly in
usage, that is, intent. They both use the same type, they both have send
methods, they both are generated the same way. If you are foolish, you can
even mix generator-behaviour and coroutine-behaviour in the same function:


py def g():
... yield 1
... yield 2
... x = yield 3
... yield x + 1000
...
py a = g()
py next(a)
1
py next(a)
2
py next(a)
3
py a.send(99)
1099

But don't do that.

So as far as Python is concerned, a coroutine is just a generator instance
which you use in a particular way, not a different kind of object. So I
would use similar terminology:

A generator function/factory returns a generator instance, which we use as a
coroutine, so we can call it a coroutine.



-- 
Steven

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


Re: generator/coroutine terminology

2015-03-12 Thread Rustom Mody
On Thursday, March 12, 2015 at 11:25:32 PM UTC+5:30, Marko Rauhamaa wrote:
 Rustom Mody :
 
  I guess we need
  1. A clear ontology of the base concepts (which is a buzzword for
  nailed-down terminology)
 
 According to the documentation, a function whose definition contains a
 yield statement is a generator:
 
Using a yield expression in a function's body causes that function to
be a generator.
 
URL: https://docs.python.org/3/reference/expressions.html#yield-exp
ressions
 
generator
   A function which returns an iterator.
 
URL: https://docs.python.org/3/glossary.html#term-generator
 
 Apparently, what we have here is a discrepancy between the documentation
 and the CPython implementation.
 
 In a word, it's a bug (somewhere).

Throwing out some thought for better terminology.
[Yeah needs to be chewed on a bit...]

With respect to my example above:

def foo(x):
   yield x+1
   yield x+2


g = foo(2) 

Generator-instance: g
Generator-factory: foo
Generator: Concept in python (ontology) elaborated in terms of the above

Situation with coroutines is worse
[Or I cant think of good terms...]
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-12 Thread Steven D'Aprano
Rustom Mody wrote:

 This is more a question about standard terminology/conventions than about
 semantics - of course assuming I understand :-)
 
 Say I have a simple yielding function:
 
 def foo(x):
yield x+1
yield x+2
 
 And I have
 
 g = foo(2)
 
 If I look at type, g's type is 'generator' whereas foo is just plain-ol
 'function.'
 
 Whereas in informal usage we say foo is a generator.

Hopefully it is clear from context what we actually mean. When in doubt, we
should be explicit.


 So the question:
 What should we call foo and what should we call g?

foo is a generator function, i.e. a function which returns a generator. I'd
also allow generator factory, naming it by intent rather than type.

The result of calling foo, namely foo(x), which you have named g, is a
generator object, which is a kind of iterator.


 Same applies when foo is a 'coroutine' ie
 something having yield used in an rhs and used with '.send' from outside:
 What to call foo and what to call foo(x)?

foo is a generator function, and foo(x) is a generator object. In this case
we can call foo(x) by usage rather than type, and call it a coroutine.



-- 
Steven

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


Re: generator/coroutine terminology

2015-03-12 Thread Rustom Mody
Guess I should be pleased that I am doing as good as you (and Chris) describe.
For some reason or not I am not...

On Thursday, March 12, 2015 at 9:58:07 PM UTC+5:30, Steven D'Aprano wrote:
 Rustom Mody wrote:
 
  This is more a question about standard terminology/conventions than about
  semantics - of course assuming I understand :-)
  
  Say I have a simple yielding function:
  
  def foo(x):
 yield x+1
 yield x+2
  
  And I have
  
  g = foo(2)
  
  If I look at type, g's type is 'generator' whereas foo is just plain-ol
  'function.'
  
  Whereas in informal usage we say foo is a generator.
 
 Hopefully it is clear from context what we actually mean. When in doubt, we
 should be explicit.

There is a very important 'context' where both have to exist together -- 
teaching beginners.

foo's are written to produce g's. g's come from foo-like.

Better naming would help clarify -- your 'factory' is the best Ive seen so far.
But the docs?!?! Hoo Boy My head spins trying to grok this
https://docs.python.org/3/reference/expressions.html#generator-expressions
And thats after being familiar with the origins of the idea in
scheme/simula/CLU etc.

Noobs could be forgiven for doing worse dont you think??

I guess we need
1. A clear ontology of the base concepts (which is a buzzword for nailed-down 
terminology)
2. Some neat pictures would sure help (me!)
-- 
https://mail.python.org/mailman/listinfo/python-list


Re: generator/coroutine terminology

2015-03-12 Thread Marko Rauhamaa
Terry Reedy tjre...@udel.edu:

 On 3/12/2015 9:35 AM, Rustom Mody wrote:
 This is more a question about standard terminology/conventions than
 about semantics - of course assuming I understand :-)

 Say I have a simple yielding function:

 def foo(x):
 yield x+1
 yield x+2

 This is a generator function

Which in the official language specification is an elaborate synonym of
a generator.


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


Re: generator/coroutine terminology

2015-03-12 Thread Terry Reedy

On 3/12/2015 9:35 AM, Rustom Mody wrote:

This is more a question about standard terminology/conventions than about 
semantics - of course assuming I understand :-)

Say I have a simple yielding function:

def foo(x):
yield x+1
yield x+2


This is a generator function


And I have

g = foo(2)

If I look at type, g's type is 'generator' whereas foo is just plain-ol 
'function.'

Whereas in informal usage we say foo is a generator.


I do not, because it does cause confusion, in spite of denials by people 
who use 'generator' ambiguously.


--
Terry Jan Reedy

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


Re: generator/coroutine terminology

2015-03-12 Thread Marko Rauhamaa
Rustom Mody rustompm...@gmail.com:

 I guess we need
 1. A clear ontology of the base concepts (which is a buzzword for
 nailed-down terminology)

According to the documentation, a function whose definition contains a
yield statement is a generator:

   Using a yield expression in a function’s body causes that function to
   be a generator.

   URL: https://docs.python.org/3/reference/expressions.html#yield-exp
   ressions

   generator
  A function which returns an iterator.

   URL: https://docs.python.org/3/glossary.html#term-generator

Apparently, what we have here is a discrepancy between the documentation
and the CPython implementation.

In a word, it's a bug (somewhere).


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