Re: [Python-ideas] Delayed Execution via Keyword

2017-02-19 Thread Pavol Lisy
On 2/19/17, Michel Desmoulin  wrote:

> Evnetually we also may need to allow this:
>
> a = lazy stuff
> if a is not lazy:
> print(a)
>
> But then lazy can't be used a var name to help with the transition.

What about this?

if not inspect.islazy(a):
print(a)

Next idea is probably obvious:

class Busy_Beaver:
''' we want to be sure that beaver is disturbed only if it is
really necessary '''
def __call_me_later__(self, n):
return too_expensive(n)
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Delayed Execution via Keyword

2017-02-19 Thread David Mertz
On Sun, Feb 19, 2017 at 10:47 AM, Joseph Hackman 
wrote:

> Your argument has convinced me, and I now take (what i believe to be) your
>> position:
>>
>
> def stuff(arg = lazy f()):
>
> should result in a function where the default value of arg is not
> evaluated until first function call, and then the value of the expression
> is used as the default.
>

Indeed.  And in particular, f() *might not* be excuted even during that
first (or any) function call, depending on what conditional path are taken
within the function body.  That's the crucial part.  The function may have
perfectly good uses where you don't want to take the computational time, or
have the side-effects, but other uses where you need that deferred value or
action.

-- 
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Delayed Execution via Keyword

2017-02-19 Thread Joseph Hackman
>
> This doesn't make sense.  Function definition time is very different than
> function execution time.  Changing that distinction is a WAY bigger change
> than I think we should contemplate.
> Moreover, there is a completely obvious way to spell the behavior you want:
> def stuff():
> arg = f()
> # ... whatever ...
> This is exactly the obvious way to spell "f() is called every time stuff()
> is".


I think it would be useful, but yeah, it really doesn't fit in with the
rest of lazy/delayed. The present format of defaulting to none and then
doing if arg is None: is totally functional.
On the flip side, doing a lazy in the function definition would save time
evaluating defaults while also fitting in.

Your argument has convinced me, and I now take (what i believe to be) your
position:

def stuff(arg = lazy f()):

should result in a function where the default value of arg is not evaluated
until first function call, and then the value of the expression is used as
the default.

Now, back to  what Michel was probably actually asking.
In the case of:

def stuff(arg = lazy []):
is the default value of arg a new list with each execution? (i.e. the
resulting value of the expression is `make a new list`)

I would say that for consistency's sake, not, which I believe would be
consistent with the logic behind why default values being [] are kept
between calls.

a = lazy []
b = a
a.append('a')
print(b) # expected behavior is ['a']

I maintain that it would be nice for there to be a way to say (the default
value of this argument is to run some expression *every time*), but
delayed/lazy probably isn't that.











On 19 February 2017 at 13:33, David Mertz  wrote:

> On Sun, Feb 19, 2017 at 10:13 AM, Joseph Hackman 
> wrote:
>>
>> My honest preference would be that the [] is evaluated fresh each time
>> the function is called.
>> def stuff(arg=delayed f()):
>> would result in f() being called every time stuff() is. This seems more
>> valuable to me than just doing it once when the function is first called.
>>
>
> This doesn't make sense.  Function definition time is very different than
> function execution time.  Changing that distinction is a WAY bigger change
> than I think we should contemplate.
>
> Moreover, there is a completely obvious way to spell the behavior you want:
>
> def stuff():
>
> arg = f()
>
> # ... whatever ...
>
>
> This is exactly the obvious way to spell "f() is called every time stuff()
> is".
>
>
> --
> Keeping medicines from the bloodstreams of the sick; food
> from the bellies of the hungry; books from the hands of the
> uneducated; technology from the underdeveloped; and putting
> advocates of freedom in prisons.  Intellectual property is
> to the 21st century what the slave trade was to the 16th.
>
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Delayed Execution via Keyword

2017-02-19 Thread David Mertz
On Sun, Feb 19, 2017 at 10:13 AM, Joseph Hackman 
wrote:
>
> My honest preference would be that the [] is evaluated fresh each time the
> function is called.
> def stuff(arg=delayed f()):
> would result in f() being called every time stuff() is. This seems more
> valuable to me than just doing it once when the function is first called.
>

This doesn't make sense.  Function definition time is very different than
function execution time.  Changing that distinction is a WAY bigger change
than I think we should contemplate.

Moreover, there is a completely obvious way to spell the behavior you want:

def stuff():

arg = f()

# ... whatever ...


This is exactly the obvious way to spell "f() is called every time stuff()
is".


-- 
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Delayed Execution via Keyword

2017-02-19 Thread Michel Desmoulin

> 
> One last thing: my vote is not dropping the ":" in front of they
> keyword.
> 
> 
> I think the colon has parser problems, as I showed in some examples. 
> Plus I don't like how it looks.  But I'd much rather have `a = lazy:
> stuff` than not have the construct at all, nonetheless.
> 
> 

This was a typo on my part. I prefer to AVOID the ":" in front of the
keyword.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Delayed Execution via Keyword

2017-02-19 Thread David Mertz
On Sun, Feb 19, 2017 at 8:24 AM, Michel Desmoulin  wrote:

> A great proposal, although now I would have to explain to my students
> the subtle difference between:
>
> res = (print(i * i) for i in range(x))
> res = delayed [print(i * i) for i in range(x)]



> They seems doing something similar, but they really don't.


Well, at the introductory level they are kinda similar. I know the
mechanism would have to be different.  But at a first brush it's the
difference between delaying the whole concrete collection and delaying one
item at a time.  That wouldn't be terrible for a first Compsci lesson.


> def stuff(arg=delayed []):
>
> Does this mean we create a NEW list every time in the body function ? Or
> just a new one the first time than the reference stays in arg ?
>

I think this cannot make a new list each time.  Of course, I'm one of those
people who have used the mutable default deliberately, albeit now it's
mostly superseded by functools.lru_cache().

But the idea of a "delayed object" is one that transforms into a concrete
(or at least *different* value) on first access.  In a sense, the
transformation from a delayed object to an iterator is still keeping it
lazy; and clearly `x = delayed my_gen()` is a possible pattern.

The pattern of `def stuff(arg=delayed expensive_computation(): ...` is
important to have.  But as in my longer example, `arg` might or might not
be accessed in the function body depending on condition execution paths.
Still, once `expensive_computation()` happens one time, that should be it,
we have a result.  Obviously `list()` is not an expensive operation, but
the syntax cannot make a boundary for "how costly."


> The "delayed" keyword sounds a lot like something used in async io, so I
> like "lazy" much more. Not only it is shorter, but it convey the meaning
> of what we are doing better.
>

I like `lazy` too.


> a = (await|yield) lazy stuff
> a = lazy (await|yield) stuff (should it even allowed ?)
> a = (lazy stuff(x) for x in stuff)
>
a = lazy f'{name}' + stuff(age) # is there a closure where we store "name"
> and 'age'?


I don't quite have a clear intuition about how lazy/delayed and
await/yield/async should interact.  I think it would be perfectly
consistent with other Python patterns if we decided some combinations
cannot be used together.  Likewise you can't write `x = await yield from
foo`, and that's fine, even though `yield from` is an expression.


> First, if there is an exception in the lazy expression, Python must
> indicate in the stack trace where this expression has been defined and
> where it's evaluated.
>

Yes.  I mentioned that there needs to be *some* way, even if it's an ugly
construct, to find out that something is delayed without concretizing it.
I think the best idea is hinted at in my preliminary thought.

I.e. we can have a special member of a delayed object that does not
concretize the object on access.  So maybe `object._delayed_code` of
something similar.  Since it's the interpreter itself, we can say that
accessing that member of the object is not a concretization, unlike
accessing any other member.  Every object that is *not* a delayed/lazy one
should probably have None for that value.  But delayed ones should have, I
guess, the closure that would get executed on access (then once accessed,
the object becomes whatever the result of the expression is, with
`._delayed_code` then set to None on that transformed object).


> a = lazy stuff
> if a is not lazy:
> print(a)
>

So my spelling would be:

a = lazy stuff

if a._delayed_code is not None:

print(a)



> One last thing: my vote is not dropping the ":" in front of they keyword.
>

I think the colon has parser problems, as I showed in some examples.  Plus
I don't like how it looks.  But I'd much rather have `a = lazy: stuff` than
not have the construct at all, nonetheless.


-- 
Keeping medicines from the bloodstreams of the sick; food
from the bellies of the hungry; books from the hands of the
uneducated; technology from the underdeveloped; and putting
advocates of freedom in prisons.  Intellectual property is
to the 21st century what the slave trade was to the 16th.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/

Re: [Python-ideas] Delayed Execution via Keyword

2017-02-19 Thread Michel Desmoulin
A great proposal, although now I would have to explain to my students
the subtle difference between:

res = (print(i * i) for i in range(x))
print('foo')
print(res)

And

res = delayed [print(i * i) for i in range(x)]
print('foo')
all(res)

They seems doing something similar, but they really don't.

Overall, I still love it.

When I read about it, I immidiatly though about how Django handles
translation in models:

- define your string in english
- mark it with ugettext_lazy and NOT ugettext
- the framework delays the translation until a request comes around with
data about the user lang

The proposed featured would solve the problem nicely.

Although I'm not clear on the result of:

def stuff(arg=delayed []):

Does this mean we create a NEW list everytime in the body function ? Or
just a new one the first time than the reference stays in arg ?

Because the first behavior would solve a problem Python had with mutable
default arguments since the begining.  But that would mean the result of
"delayed []" is a concrete thing we store in arg.

The "delayed" keyword sounds a lot like something used in async io, so I
like "lazy" much more. Not only it is shorter, but it convey the meaning
of what we are doing better.

Talking about async, we need to be clear on what those do:

a = (await|yield) lazy stuff
a = lazy (await|yield) stuff (should it even allowed ?)
a = (lazy stuff(x) for x in stuff)
a = None
with open(x) as f:
a = lazy stuff() # raise IOError
print(a)

try:
a = lazy stuff() # raise
except Exception:
pass

a = lazy f'{name}' + stuff(age) # is there a closure where we store
"name"  and 'age'?

I can see a reasonable outcome for most of this, but it must be very clear.

However, I can see several very important things we need to be taking in
consederation debugging wise.

First, if there is an exception in the lazy expression, Python must
indicate in the stack trace where this expression has been defined and
where it's evaluated.

Pdb must also be able to allow easily to step in those in a coherent manner.

Evnetually we also may need to allow this:

a = lazy stuff
if a is not lazy:
print(a)

But then lazy can't be used a var name to help with the transition.

One last thing: my vote is not dropping the ":" in front of they keyword.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/


Re: [Python-ideas] Light-weight call-by-name syntax in Python

2017-02-19 Thread Ed Kellett
On Fri, 17 Feb 2017 at 10:23 Stephan Houben  wrote:

> Proposal: Light-weight call-by-name syntax in Python
>
>   The following syntax
>  a : b
>   is to be interpreted as:
>  a(lambda: b)
>
> Effectively, this gives a "light-weight macro system" to Python,
> since it allows with little syntax to indicate that the argument to
> a function is not to be immediately invoked.
>

This is, in my view, one case where Python's existing lambda syntax is
perfectly sufficient. (I'd even argue it might be the *only* case...) If
you could logger.debug(lambda: expensive_to_compute_message_here), I don't
think that the delayed-expression proposal would ever have existed.

Ed
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/