Re: [Python-ideas] Add new `Symbol` type

2018-07-05 Thread Ed Kellett
Hi!

On 2018-07-05 20:38, Flavio Curella wrote:
> More than once I've found myself wanting to create a 'sentinel' value. The
> most common use case is to differentiate between an argument that has not
> been provided, and an argument provided with the value `None`.

I generally do something like

_nothing = object()

> Furthermore, without a common implementation in the std library, various
> Python libraries had to write their own implementations, which all differs
> in functionality and behavior.

What functionality does such a thing actually need?
___
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] "given" vs ":=" in list comprehensions

2018-05-14 Thread Ed Kellett
On 2018-05-14 05:02, Nick Coghlan wrote:
> The same grammar adjustment that I believe will allow "given" to be used as
> both a postfix keyword and as a regular name would also work for "where".
> However, "where" still has the problem of semantically conflicting with
> SQL's use of it to introduce a filter clause, whereas Hypothesis uses
> "given" to bind names to values (just a little more indirectly than would
> be the case for assignment expressions)

I suspect that SQL is not high on the list of languages people might
confuse with Python. FWIW, as I'm sure will have been mentioned, Haskell
uses "where", and people seem to manage fine with it.



signature.asc
Description: OpenPGP digital signature
___
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] Crazy idea: allow keywords as names in certain positions

2018-05-14 Thread Ed Kellett
On 2018-05-14 12:35, Rhodri James wrote:
> On 13/05/18 19:19, Guido van Rossum wrote:
>> The idea I had (not for the first time:-)  is that in many syntactic
>> positions we could just treat keywords as names, and that would free up
>> these keywords.
> 
> I'm not familiar with the innards of the parser and it's wy too long
> since I sat through a parsing course, but can you turn that inside out?
> Are there times when the compiler knows it must be looking at a keyword,
> not a name?  I suspect not, given that arbitrary expressions can be
> statements, but someone else may have a more knowledgeable opinion.

Sure are. For example, just after `1 `, a name would be an error, while
a keyword would be fine. But, more to the point, there are times when
the parser knows (or could know) it can't be looking at a certain
keyword. Suppose you want to parse at the beginning of an expression
when you see "and". Currently, you're duty-bound to explode, because
"and" cannot begin an expression. You *could* equally well not explode
and, knowing that "and" cannot begin an expression, interpret it
completely unambiguously as a name.



signature.asc
Description: OpenPGP digital signature
___
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] Inline assignments using "given" clauses

2018-05-12 Thread Ed Kellett
On 2018-05-12 22:42, Chris Angelico wrote:
> It's documented. It's guaranteed not to change.
> 
> https://docs.python.org/3/reference/expressions.html#evaluation-order

Thanks. That's good to know, though I continue to hope nobody makes a
habit of writing code that depends on it.



signature.asc
Description: OpenPGP digital signature
___
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] Inline assignments using "given" clauses

2018-05-12 Thread Ed Kellett
On 2018-05-12 18:24, Steven D'Aprano wrote:
> On Sat, May 12, 2018 at 08:16:07AM -0700, Neil Girdhar wrote:
>> I love given compared with := mainly because
>> 
>> Simpler is better than complex: * given breaks a complex statement
>>  into two simpler ones,
> 
> [snip]
> 
> Nick's syntax is *much more complicated* than the := syntax. Any 
> arbitrary expression can appear on the left of "given". It need not 
> even involve the binding target! So to make a fair comparison, I 
> ought to compare:
> 
> target := expr
> 
> which evaluates a single expression, binds it, and returns it, to:
> 
> another_expr given target := expr
> 
> which evaluates "expr", binds it to "target", evaluates a SECOND 
> unrelated expression, and returns that.
> 
> If you want to argue that this is more useful, then fine, say so.
> But to say that it is *simpler* makes no sense to me.
> 
> Option 1: evaluate and bind a single expression
> 
> Option 2: exactly the same as Option 1, and then evaluate a second 
> expression
> 
> How do you justify that Option 2 "given", which does everything := 
> does PLUS MORE, is simpler than Option 1?

Your interpretation of the argument you quote, as I understand it, is
that "`given` is simpler than `:=`". I interpreted it as, and would
argue myself, more like "`given` makes *the code that uses it* simpler".

The reason the `given` syntax might be simpler has nothing to do with
the number of syntactic elements (brainfuck is down the hall...).
Rather, it is that `given` separates things:

if m.group(2) in words given m = word_re.match(s):

versus:

if (m := word_re.match(s)).group(2) in words:

In the `:=` version, the assignment is embedded in the expression. It's
different. Most of the time it will save at least a few characters. But
it's not obviously--and certainly not objectively--simpler.

One somewhat concrete difference I can think of is that in expressions
that refer to the result multiple times, the `:=` assignment must always
be positioned such that it is the first to be evaluated, moving around
should that ever change. In cases where evaluation depends on runtime
information (if-else, and, or), I'm not actually sure what you would do.

In general, I feel like you're focusing on the simplicity or otherwise
of the operator itself, rather than whether it has a simplifying effect
on code that uses it. As you say in part 2 (which arrived while I was
still taking forever to write this; sorry if it reads a bit confused),
we'll learn the syntax, eventually. What's not so explicitly spelled out
is that we'll be reading new code that uses it forever.

> average = 0 smooth_signal = [(average := (1-decay)*average +
> decay*x) for x in signal] assert average == smooth_signal[-1]

Like, holy smoke, man. Sure, `:=` is probably better for cramming
side-effects into list comprehensions. Please don't cram side-effects
into list comprehensions.

> total = 0 running_totals = [total := total + x for x in xs]
> 
> versus
> 
> total = 0 running_totals = [total given total = total + x for x in xs]

What is it with the cramming side-effects into list comprehensions‽ Do
you realise itertools.accumulate exists?

See the OP:

On 2018-05-04 13:06, Nick Coghlan wrote:
> 3. Sharing values between filtering clauses and result expressions in
> comprehensions:
> 
> result = [(x, y, x/y) for x in data if (y := f(x))]

I don't have a more concrete example of this to hand, mostly because I
can't do this today, and faking it is hard enough (I didn't even know
about the `for value in [stuff]` hack until this thread) that I just
write for loops.

On 2018-05-12 18:24, Steven D'Aprano wrote:
> By your argument, augmented assignment is more complex, and we ought 
> to prefer splitting it into two separate operations x = x + 1 because
> that's simpler.
> 
> I think I agree that x = x + 1 *is* simpler. We can understand it by 
> understanding the two parts separately: x+1, followed by assignment.
> 
> Whereas += requires us to understand that the syntax not only calls
> a dunder method __iadd__ (or __add__ if that doesn't exist), which 
> potentially can operate in place, but it also does an assignment,
> all in one conceptual operation. There's a whole lot of extra
> complexity there.

While I'm not sorry that I can do `x += 1`, it does have a substantial
cost--one that is perhaps justified by the fact that it's *not*
equivalent to `x = x + 1`, and that the possibilities it opens up are
both useful and easy to understand. It's not simpler in and of itself,
but that's not really the issue.

On 2018-05-12 19:27, Steven D'Aprano wrote:
> What you probably want is the second version:
> 
> (a := c.d()).b(a)
> 
> which of course looks like utter crap. It might look better if we
> use at least half-way sensible variable names and a more realistic 
> looking example, instead of obfuscated one-letter names.
> 
> (widget := widget_builder.new(*args)).method(widget)

I believe that that's missing the point: to wit, in

x.method(y

Re: [Python-ideas] Inline assignments using "given" clauses

2018-05-11 Thread Ed Kellett
On 2018-05-11 22:12, Angus Hollands wrote:
> while (value:=get_next_pool_item()) and value.in_use:
> print(value.refcount())

Just as a heads-up, I believe the prescribed way of doing that is:

while (value := get_next_pool_item()).in_use:

Of course you'd need additional mess to do something else with value. I
don't like the asymmetry here:

while (value := get_next_pool_item()).in_use and value is not blah:

> Secondly, it reads in the order that matters. When reading the first line,
> one encounters what the condition is evaluating *first*, and then the
> implementation details (m=p.match) second. It reads as one would describe a
> mathematical equation in a paper, and clearly separates *what you're
> interested in* from *what it depends upon*. This is what I particularly
> dislike about the ":=" operator approach, the value, and name it is bound
> to, are unrelated at the point of evaluation, yet are right next to each
> other. It's visual noise. In the example above, the reader has to read the
> entire line when trying to find the loop condition.

I'm inclined to agree. But several people have argued that this is more
readable than the alternative. I don't buy the reasoning, but they still
like it better, and there's probably no point in going any further into
this aspect. I doubt people are going to be convinced.
> What am I getting at here? In effect, the "given" keyword provides a
> superset of use cases to that of ":=". Dare I say it, but *explicit is
> better than implicit*.

I'm not sure that it's strictly a superset. It's arguably the reverse,
since it's restricted to statements with a condition rather than
arbitrary expressions. I think the more important thing is that
it's--subjectively--better at the subset of use cases that people seem
to actually have (as listed in the OP).

> *Readability:*
> A smaller point is that I don't feel that ":=" is very readable. If we had
> to use an operator, I think $= is better, but me reasoning for this is
> weak. I think it derives from my observation that ":=" is slow to
> distinguish from "=".

Clearly the objectively best choice is "<-".



signature.asc
Description: OpenPGP digital signature
___
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] Inline assignments using "given" clauses

2018-05-11 Thread Ed Kellett
On 2018-05-10 17:10, Gustavo Carneiro wrote:
> IMHO, all these toy examples don't translate well to the real world because
> they tend to use very short variable names while in real world [good
> written code] tends to select longer more descriptive variable names.
> 
> Try replacing "c" with  a longer name, like input_command, then it becomes:
> 
> while ((input_command = getchar()) != EOF) ...
> 
> while (input_command = getchar(), input_command != EOF) ...
This thread really isn't about variable naming. I'll simply say that
I've always written the C idiom with `c` as the variable name, I've seen
literally hundreds of others do the same, and I've never seen anyone
spell it `input_command`.

I do the same in Python in a few contexts, usually where the variable's
meaning is very clear from its usage, or where the code in question
doesn't know or care what the variable is used for:

for i in range(10):
def get_const(self, x):
for k in self.defaults.keys():

etc., and if we had `given` clauses, I'd probably do this there too. I
think one of the advantages of that syntax is that it makes it extremely
clear what's going on:

if m given m = re.match(...):

I don't need to try to stuff an English description of m into its name,
because the `given` clause already describes it perfectly.



signature.asc
Description: OpenPGP digital signature
___
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] Inline assignments using "given" clauses

2018-05-10 Thread Ed Kellett
On 2018-05-10 16:10, Guido van Rossum wrote:
> Please no, it's not that easy. I can easily generate a stream of +1s or -1s
> for any proposal. I'd need well-reasoned explanations and it would have to
> come from people who are willing to spend significant time writing it up
> eloquently. Nick has tried his best and failed to convince me. So the bar
> is high.
> 
> (Also note that most of the examples that have been brought up lately were
> meant to illustrate the behavior in esoteric corner cases while I was
> working out the fine details of the semantics. Users should use this
> feature sparingly and stay very far away of those corner cases -- but they
> have to be specified in order to be able to implement this thing.)

Poor prospects, then, but I'll do my best.

I think the most obvious argument (to me) favouring `given` over `:=` is
that it separates the two things it's doing:

if m.group(2) given m = pattern.search(data):

as opposed to the more-nested := version:

if (m := pattern.search(data)).group(2):

which, at least to me, is more complicated to think about because it
feels like it's making the .group() something to do with the assignment.

Put another way, I think your use of parentheses when discussing the
*pronunciation* of this thing is telling. It feels as though one needs
to start in the middle and then go in both directions at once, first
explaining the origin (or destination) of the operand in question in a
parenthesized offshoot, and then switching context and describing what
is done to it. It's midly mentally taxing. I'm sure we can all live with
that, but I don't want to: Python's exceptionally-readable syntax is one
of the bigger reasons I choose it.

There's a striking parallel in C, where the well-known idiom:

while ((c = getchar()) != EOF) ...

has an obviously-nicer alternative:

while (c = getchar(), c != EOF) ...

Most people I show this to agree that it's nicer, despite the fact that
it manages to repeat a variable name *and* use the comma operator. I
don't have proof, but I'd suggest that unwrapping that layer of context
for the reader imparts a significant benefit.

The C example also provides a convenient test: if you think the former
example is nicer, I can just give up now ;)



signature.asc
Description: OpenPGP digital signature
___
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] Pattern Matching Syntax

2018-05-04 Thread Ed Kellett
On 2018-05-04 08:26, Jacco van Dorp wrote:
> Would this be valid?
> 
> # Pattern matching with guards
> x = 'three'
> 
> number = match x:
> 1 => "one"
> y if y is str => f'The string is {y}'
> z if z is int => f'the int is {z}'
> _ => "anything"
> 
> print(number)  # The string is three
> 
> If so, why are y and z both valid here ? Is the match variable rebound
> to any other ? Or even to all names ?

In the match case here:

match x:
y if y > 3 => f'{y} is >3' # to use an example that works

there are three parts:

"y" is a pattern. It specifies the shape of the value to match: in this
case, anything at all. Nothing is bound yet.
"if" is just the word if, used as a separator, nothing to do with "if"
in expressions.
"y > 3" is the guard expression for the match case. Iff the pattern
matches, "y > 3" is evaluated, with names appearing in the pattern
taking the values they matched.

It's important to note that the thing on the left-hand side is
explicitly *not* a variable. It's a pattern, which can look like a
variable, but it could also be a literal or a display.

> ofc, you could improve the clarity here with:
> 
> number = match x as y:
> 
> or any variant thereof. This way, you'd explicitely bind the variable
> you use for testing. If you don't, the interpreter would never know
> which ones to treat as rebindings and which to draw from surrounding
> scopes, if any.

I don't think anything in the pattern should be drawn from surrounding
scopes.

> I also haven't really seen a function where this would be better than
> existing syntax, and the above is the only one to actually try
> something not possible with dicts. The type checking one could better
> be:
> 
> [snip]
> 
> The production datetime code could be:
> 
> def convert_time_to_timedelta_with_match(unit:str, amount:int, now:date):
>return {
> "days":timedelta(**{unit: amount}),
> "hours":timedelta(**{unit: amount}),
> "weeks":timedelta(**{unit: amount}),
> # why not something like subtracting two dates here to get an
> accurate timedelta for your specific interval ?
> "months":timedelta(days = 30*amount),  # days = (365.25 /
> 12)*amount ? Would be a lot more accurate for average month length.
> (30.4375)
> "years":timedelta(days=365*amount),  # days = 365.25*amount ?
> "cal_years":timedelta(now - now.replace(year=now.year - amount)),
>   }.get(unit)

Don't you think the repetition of ``timedelta(**{unit: amount})'' sort
of proves OP's point?

Incidentally, there's no need to use the dict trick when the unit is
known statically anyway. I can't decide if that would count as more
reptition or less.

> I honestly don't see the advantages of new syntax here.
> Unless you hate the eager evaluation in the dict literal getting
> indexed, so if it's performance critical an if/else might be better.
> But I can't see a match statement outperforming if/else. (and if you
> really need faster than if/else, you should perhaps move that bit of
> code to C or something.)

It's not really about performance. It's about power. A bunch of if
statements can do many things--anything, arguably--but their generality
translates into repetition when dealing with many instances of this
family of cases.



signature.asc
Description: OpenPGP digital signature
___
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] Pattern Matching Syntax

2018-05-03 Thread Ed Kellett
On 2018-05-03 20:17, Chris Angelico wrote:
>> def convert_time_to_timedelta_with_match(unit:str, amount:int, now:date):
>>  return match unit:
>>  x if x in ('days', 'hours', 'weeks') => timedelta(**{unit: amount})
>>  'months' => timedelta(days=30 * amount)
>>  'years' => timedelta(days=365 * amount)
>>  'cal_years' => now - now.replace(year=now.year - amount)
> 
> And then this comes down to the same as all the other comparisons -
> the "x if x" gets duplicated. So maybe it would be best to describe
> this thus:
> 
> match  :
>  | ( ) => 
> 
> If it's just an expression, it's equivalent to a comp_op of '=='. The
> result of evaluating the match expression is then used as the left
> operand for ALL the comparisons. So you could write your example as:
> 
> return match unit:
> in ('days', 'hours', 'weeks') => timedelta(**{unit: amount})
> 'months' => timedelta(days=30 * amount)
> 'years' => timedelta(days=365 * amount)
> 'cal_years' => now - now.replace(year=now.year - amount)
> 
> Then there's room to expand that to a comma-separated list of values,
> which would pattern-match a tuple.

I believe there are some problems with this approach. That case uses no
destructuring at all, so the syntax that supports destructuring looks
clumsy. In general, if you want to support something like:

match spec:
(None, const) => const
(env, fmt) if env => fmt.format(**env)

then I think something like the 'if' syntax is essential for guards.

One could also imagine cases where it'd be useful to guard on more
involved properties of things:

match number_ish:
x:str if x.lower().startswith('0x') => int(x[2:], 16)
x:str => int(x)
x => x  #yolo

(I know base=0 exists, but let's imagine we're implementing base=0, or
something).

I'm usually against naming things, and deeply resent having to name the
x in [x for x in ... if ...] and similar constructs. But in this
specific case, where destructuring is kind of the point, I don't think
there's much value in compromising that to avoid a name.

I'd suggest something like this instead:

return match unit:
_ in {'days', 'hours', 'weeks'} => timedelta(**{unit: amount})
...

So a match entry would be one of:
- A pattern. See below
- A pattern followed by "if" , e.g.:
  (False, x) if len(x) >= 7
- A comparison where the left-hand side is a pattern, e.g.:
  _ in {'days', 'hours', 'weeks'}

Where a pattern is one of:
- A display of patterns, e.g.:
  {'key': v, 'ignore': _}
  I think *x and **x should be allowed here.
- A comma-separated list of patterns, making a tuple
- A pattern enclosed in parentheses
- A literal (that is not a formatted string literal, for sanity)
- A name
- A name with a type annotation

To give a not-at-all-motivating but hopefully illustrative example:

return match x:
(0, _) => None
(n, x) if n < 32 => ', '.join([x] * n)
x:str if len(x) <= 5 => x
x:str => x[:2] + '...'
n:Integral < 32 => '!' * n

Where:
(0, 'blorp')would match the first case, yielding None
(3, 'hello')would match the second case, yielding
"hello, hello, hello"
'frogs' would match the third case, yielding "frogs"
'frogs!'would match the fourth case, yielding "fr..."
3   would match the fifth case, yielding '!!!'

I think the matching process would mostly be intuitive, but one detail
that might raise some questions: (x, x) could be allowed, and it'd make
a lot of sense for that to match only (1, 1), (2, 2), ('hi', 'hi'), etc.
But that'd make the _ convention less useful unless it became more than
a convention.

All in all, I like this idea, but I think it might be a bit too heavy to
get into Python. It has the feel of requiring quite a lot of new things.



signature.asc
Description: OpenPGP digital signature
___
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] Pattern Matching Syntax

2018-05-03 Thread Ed Kellett
On 2018-05-03 19:57, Chris Angelico wrote:
> Got it. Well, I don't see why we can't use Python's existing primitives.
> 
> def hyperop(n, a, b):
> if n == 0: return 1 + b
> if n == 1: return a + b
> if n == 2: return a * b
> if n == 3: return a ** b
> if n == 4: return a *** b
> if n == 5: return a  b
> if n == 6: return a * b
> ...

Well, it'd be infinitely long, but I suppose I'd have to concede that
that's in line with the general practicality level of the example.



signature.asc
Description: OpenPGP digital signature
___
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] Pattern Matching Syntax

2018-05-03 Thread Ed Kellett
On 2018-05-03 18:53, Rhodri James wrote:
> On 03/05/18 18:18, Ed Kellett wrote:
>> It's a proposal for new syntax.
> 
> I snipped the rest because fundamentally you have failed to explain your
> new syntax in any clear way.  You've given examples of varying levels of
> complexity but failed to explain what any of them should actually do in
> words.  It wasn't even obvious from your introduction that you were
> talking about match *expressions* rather than switch statements.
> 
> Sorry, but this is too unclear to comment on at the moment.

It's not my new syntax.



signature.asc
Description: OpenPGP digital signature
___
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] Pattern Matching Syntax

2018-05-03 Thread Ed Kellett
On 2018-05-03 15:02, Jacco van Dorp wrote:
>> # Pattern matching with guards
>> x = 'three'
>>
>> number = match x:
>>1 => "one"
>>y if y is str => f'The string is {y}'
>>_ => "anything"
>>
>> print(number)  # The string is three
> 
> I think you meant to use isinstance(y, str) ?
> This looks like an incomplete ternary as well, missing the else
> clause, so it wouldn't be a valid expression either way.
> And a NameError. y is never defined anywhere. Since there's no name
> bound to the variable being matched, would that mean a new keyword ?
> Also, in the rest you seem to be testing "x == {matchidentifier}", but
> here it suddenly looks like a boolean True would trigger a match ? And
> if so, would other boolean truthy values also trigger one, making the
> entire construct ratherlimited ?
> 
> It looks like you're attempting to suggest at least 3 new language
> syntax elements here. 4 if I count the type matching of "x:int", which
> you could sell as type annotation, if those hadn't been explicitly
> optional and ignored when actually binding the names.

It's a proposal for new syntax. I suspect that you're trying to read the
left-hand side of the match cases as Python expressions. They're a
distinct thing: unbound names like 'y' are an essential component of any
non-trivial destructuring pattern match, as opposed to an error in an
expression.

I believe the intention in the example you quoted is syntax something like:

 ::= 
   |  "if" 

where the expression is a guard expression evaluated in the context of
the matched pattern. IOW, it could be written like this, too:

number = match x:
1 if True => "one"
y if isinstance(y, str) => f'The string is {y}'
_ if True => "anything"

I do see a lot of room for bikeshedding around the specific spelling.
I'm going to try to resist the temptation ;)

> And almost every other example can be solved with a dict and .get().
> The remainder uses a dict as match, and still work on if/elif
> perfectly fine.

How about this?

def hyperop(n, a, b):
return match (n, a, b):
(0, _, b) => b + 1
(1, a, 0) => a
(2, _, 0) => 0
(_, _, 0) => 1
(n, a, b) => hyperop(n-1, a, hyperop(n, a, b-1))

versus:

def hyperop(n, a, b):
if n == 0:
return b + 1
if n == 1 and b == 0:
return a
if n == 2 and b == 0:
return 0
if b == 0:
return 1
return hyperop(n-1, a, hyperop(n, a, b-1))

Of course the latter *works* (sort of: implementing tetration by
recursively adding ones never really works) but it's excessively
verbose, it's easy to make mistakes when writing it out, and at least in
my opinion it's harder to see what it does.

It also raises some small but annoying questions, like "do I use if or
elif?", "do I use an if for the last case too?", "do I nest the b == 0
cases?", and, if we had used an if for the last case, what we do if we
get to the end anyway.

> Also, I'd say "but other people do it" isn't a valid reason for
> implementation. There's plenty people doing stupid things, that
> doesn't mean it's a good idea to do it to. If they idea can't stand on
> it's own, it's not worth it.
> 
> From the syntax corner, it also doesn't really look like Python to me.

I agree, but I'm sure someone can come up with something prettier.



signature.asc
Description: OpenPGP digital signature
___
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] A "local" pseudo-function

2018-04-28 Thread Ed Kellett
On 2018-04-28 21:40, Tim Peters wrote:
> [Ed Kellett ]
>> How about if you just can't have an expression in a local()?
> 
> See the quadratic equation example in the original post.  When 
> working with expressions, the entire point of the construct is to 
> define (sub)local names for use in a result expression.
> 
> [snip]
> 
> Requiring "in" requires annoying syntactic repetition in the common
> 
> if local(match=re.match(regexp, line)) in match:
> 
> kinds of cases.

I don't mean "in" should be required, but rather that the last thing is
always an assignment, and the local() yields the assigned value.

So that'd remain:

if local(match=re.match(regexp, line)):

whereas your quadratic equation might do the "in" thing:

r1, r2 = local(D = b**2 - 4*a*c,
   sqrtD = math.sqrt(D),
   twoa = 2*a) in (
   (-b + sqrtD)/twoa, (-b - sqrtD)/twoa)

... which doesn't look particularly wonderful (I think it's nicer with
my option 3), but then I've never thought the quadratic equation was a
good motivating case for any version of an assignment expression.

In most cases I can imagine, a plain local() would be sufficient, e.g.:

if local(match=re.match(regexp, line)) and match[1] == 'frog':

> Making "in" optional instead was discussed near the end of the 
> original post.  I agree that your spelling just above is more obvious
> than `local(x=1, y=2, x+y)` which is why the original post discussed
> making an "in clause" optional.  But, overwhelmingly, it appears that
> people are more interested in establishing sublocal scopes in `if`
> and `while` constructs than in standalone expressions, so I picked a
> spelling that's _most_ convenient for the latter's common "just name
> a result and test its truthiness" uses.
> 
> [snip]
> 
> Indeed, 3 is the only one I'd consider, but I don't see that it's a 
> real improvement.  It seems to require extra typing every time just 
> to avoid learning "and it returns the value of the last expression" 
> once.

I agree with pretty much all of this--I was trying to attack the "but
you could make the terrible C mistake" problem from another angle. I
don't think I mind the last "argument" being *allowed* to be an
expression, but if the consensus is that it makes '='/'==' confusion too
easy, I think it'd be more straightforward to say they're all
assignments (with optional, explicit syntax to yield an expression) than
to say "they're all assignments except the last thing" (argh) "except in
the special case of one argument" (double argh).



signature.asc
Description: OpenPGP digital signature
___
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] A "local" pseudo-function

2018-04-28 Thread Ed Kellett
On 2018-04-28 18:16, Tim Peters wrote:
> [Steven D'Aprano ]
>> But the biggest problem is that this re-introduces exactly the same
>> awful C mistake that := was chosen to avoid. Which of the following two
>> contains the typo?
>>
>> local(spam=expression, eggs=expression, cheese = spam+eggs)
>>
>> local(spam=expression, eggs=expression, cheese == spam+eggs)
> 
> [snip]
> 
> Still, if people are scared of that, a variation of Yury's alternative
> avoids it:  the last "argument" must be an expression (not a binding).
> In that case your first line above is a compile-time error.
> 
> I didn't like that because I really dislike the textual redundancy in the 
> common
> 
> if local(matchobject=re.match(regexp, line), matchobject):
> 
> compared to
> 
> if local(matchobject=re.match(regexp, line)):
> 
> But I could compromise ;-)
> 
> - There must be at least one argument.
> - The first argument must be a binding.
> - All but the last argument must also be bindings.
> - If there's more than one argument, the last argument must be an expression.

How about if you just can't have an expression in a local()? There are a
few obvious alternative possibilities:

1. No special case at all:

local(x=1, y=2, _=x+y)

2. Co-opt a keyword after local():

local(x=1, y=2) in x+y

3. Co-opt a keyword inside local():

local(x=1, y=2, return x+y)

I hate the first and wish the _ pattern would die in all its forms, but
it's worth mentioning. I don't think there's much to choose between the
other two, but 2 uses syntax that might have been valid and meant
something else, so 3 is probably less confusing.



signature.asc
Description: OpenPGP digital signature
___
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] A "local" pseudo-function

2018-04-28 Thread Ed Kellett
On 2018-04-28 18:36, Chris Angelico wrote:
> This makes reasonable sense. The parentheses completely enclose the
> local scope. It's compiler magic, and you cannot explain it as a
> function call, but it makes intuitive sense. But the same thing inside
> the if header itself would be much weirder. I'm actually not even sure
> what it would do. And you've clearly shown that the local() call can
> be anywhere inside the condition, based on these examples:

What if the names persist until the end of the statement? That covers if
(where the statement lasts until the end of the if...elif...else block)
and regular expressions, though it does introduce a potentially annoying
shadowing thing:

>>> x = 2
>>> (local(x=4, x + 1), x)
(5,4)



signature.asc
Description: OpenPGP digital signature
___
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] Add the method decorator

2018-04-12 Thread Ed Kellett
On 2018-04-12 14:57, Serhiy Storchaka wrote:
> If noddy_name is a Python function, noddy.name() will call
> noddy_name(noddy), but if it is a C function, noddy.name() will call
> noddy_name().
> 
> The same is true for classes and custom callables.

FWIW, you could (almost) do this in py2:

>>> class Str(str): pass
...
>>> Str.print = types.MethodType(print, None, Str)
>>> Str("hello").print()
hello

> If a function is a descriptor, it can be converted into non-descriptor
> function by wrapping it with the staticmethod decorator. I suggest to
> add the method decorator, which converts an rbitrary callable into a
> descriptor.
> 
>     class Noddy:
>     name = method(noddy_name)
> 
> This will help to implement only performance critical method of a class
> in C.

Does the method decorator need to be written in C for the performance
benefit? If you can stand __get__ being Python, it's pretty easy to
write and doesn't need to change the language.

This does remind me of my favourite silly functional Python trick: as
long as foo is implemented in Python, foo.__get__(x)(y,z) is equivalent
to foo(x,y,z), which is useful if you find Python's standard partial
application syntax too ugly.

> Currently you need to implement a base class in C, and inherit
> Python class from C class. But this doesn't work when the class should
> be inherited from other C class, or when an existing class should be
> patched like in total_ordering.
> 
> This will help also to use custom callables as methods.

I wonder if it wouldn't make more sense to make the behaviour consistent
between Python and C functions... that

someclass.blah = a_python_function

already does a frequently-wrong thing suggests (to me) that maybe the
proper solution would be to bring back unbound method objects.

Ed



signature.asc
Description: OpenPGP digital signature
___
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] Accepting multiple mappings as positional arguments to create dicts

2018-04-12 Thread Ed Kellett
On 2018-04-12 14:46, Andrés Delfino wrote:
> Extending the original idea, IMHO it would make sense for the dict
> constructor to create a new dictionary not only from several mappings, but
> mixing mappings and iterables too.
> 
> Consider this example:
> 
> x = [(1, 'one')]
> y = {2: 'two'}
> 
> Now: {**dict(x), **y}
> Proposed: dict(x, y)
> 
> I think this extension makes the call ostensibly easier to read and grep.

It allows for creating a flattened dict from an iterable of dicts, too,
which I've occasionally wanted:

>>> configs = {'a': 'yes'}, {'b': 'no'}, {'c': 3}
>>> dict(*configs)
{'a': 'yes', 'b': 'no', 'c': 3}

versus:

>>> dict(chain.from_iterable(c.items() for c in configs))
{'a': 'yes', 'b': 'no', 'c': 3}

Ed



signature.asc
Description: OpenPGP digital signature
___
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] Arguments to exceptions

2017-07-05 Thread Ed Kellett
Hi,

On Wed, 5 Jul 2017 at 16:41 Steven D'Aprano  wrote:

> and more. Third parties *are* providing rich exception APIs where it
> makes sense to do so, using the interface encouraged by PEP 352 (named
> attributes), without needing a default "StructuredException" in the
> core language.
>

Your arguments might be used to dismiss anything. If people are already
doing $thing, clearly they don't need help from the language. If they're
not already doing it, any language feature would be pointless.

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/


Re: [Python-ideas] get() method for list and tuples

2017-03-05 Thread Ed Kellett
On Sun, 5 Mar 2017 at 19:54 David Mertz  wrote:

> In terms of an actual use case, I can see it for "Lists no longer than 4".
>

That's an excessively hard limit.


> Any other use of this hypothetical method would be an anti-pattern
>

What really is the point of this? You (not uniquely) have been quick to
dismiss any uses for this as misguided. If you must do that, you might
stick to the reasons; meaningless labels don't add anything.


> Yes, programmers can do what they want, but providing a method is a hint
> to users (especially beginners, but not only) that that is the "right way"
> to do it.
>
>
> I really think that depends what it's a list of. If the positions of
> things in the list are important (as with an argument parser, or perhaps a
> lookup table) I fail to see why it would be wrong to peek.
>
>
> But the positions NEVER are important when you get to a 20 item list.  If
> you design an argument parser that is looking for "the 17th argument" you
> are doing it wrong.  I'm not saying that's impossible (nor even hard) to
> program, but it's not good practice.
>

That's probably true of argument parsers, certainly not lookup tables.


> Sure, I'm happy to take 20+ arguments, especially if they result from a
> glob pattern used at the command line, naming files.  But when I'm doing
> that, I want to deal with those filenames in a loop, handling each one as
> necessary.  In that pattern, I *never* want exactly 20 arguments, but
> rather "however many things there are to handle."
>
>
> If lists were really designed to be used only as you and some others in
> this thread are suggesting, I don't think they'd have indexed access at all.
>
>
> Actually, when I teach I make a big point of telling students (for me,
> professional scientists and programmers who have used other PLs) that if
> they are indexing a list they should look again and question whether that's
> the right pattern.  Of course there are times when it's needed, but they
> are fewer than C, Java, or Fortran programmers think.
>

I don't think that assessment applies neatly everywhere. Indexing is
generally unnecessary when it's being used instead of iteration, but this
thread is explicitly about cases where iteration isn't wanted.

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/

Re: [Python-ideas] get() method for list and tuples

2017-03-05 Thread Ed Kellett
On Sun, 5 Mar 2017 at 18:13 David Mertz  wrote:

> But if the next line is:
>
> data = args.get(17, "")
>
> Then I'm pretty sure the programmer thinks she's being passed a very
> different type of collection than is actually available.  I'd rather that
> fails right away and in an obvious way then silently produce a value.
>

That's up to the programmer. args[17] exists and does fail immediately. If
the programmer provides a default value, presumably they know they want one.


> Specifically, if I think I'm dealing with a list that is likely to have 20
> items (rather than maybe 4 or fewer), I'm almost sure the best way to deal
> with it is in a loop (or comprehension, map(), etc) and NOT by poking into
> large index positions that may or may not be present.
>

I really think that depends what it's a list of. If the positions of things
in the list are important (as with an argument parser, or perhaps a lookup
table) I fail to see why it would be wrong to peek.

If lists were really designed to be used only as you and some others in
this thread are suggesting, I don't think they'd have indexed access at all.

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/

Re: [Python-ideas] get() method for list and tuples

2017-03-05 Thread Ed Kellett
On Sun, 5 Mar 2017 at 14:08 Paul Moore  wrote:

> Self-evidently no. But what does that prove? That we should implement
> list.get? You could use the dientical argument for *anything*. There
> needs to be another reason for implementing it.
>

I don't think that's true. It's not true for many other list methods, for
example. It's not a reason for implementing it, but it does suggest to me
that it would increase the one-obvious-way-to-do-it-ness of the language.

But you have yet to explain why you'd be glad not to write a helper
> for list.get, in any terms that don't simply boil down to "someone
> else did the work for me".
>

What? Five lines isn't work, it's just ugly. I don't want to add a lot of
random utility functions like this because it drastically increases the
effort required to read my code. It's hardly any effort, but it doesn't
solve any problems, so why bother?

The point about this as a Python change is that it's a standard. Who does
the work couldn't be less relevant; what matters is that it would add a
consistent and easy spelling for something that doesn't have one.


> I think we're going to have to just disagree. You won't convince me
> it's worth adding list.get unless you can demonstrate some *existing*
> costs that would be removed by adding list.get, and showing that they
> are greater than the costs of adding list.get (search this list if you
> don't know what those costs are - I'm not going to repeat them again,
> but they are *not* trivial).


They seem to be "it'd need to be added to Sequence too" and "it would mess
with code that checks for a .get method to determine whether something is a
mapping". It's easily implementable in Sequence as a mixin method, so I'm
prepared to call that trivial, and I'm somewhat sceptical that the latter
code does—let alone should—exist.


> And I don't seem to be able to convince
> you that writing a helper function is a reasonable approach.
>

I feel like I'm saying this a lot, but writing helper functions has its own
readability cost. I'm not trying to get anyone to implement list.get, I'm
trying to get it centrally documented and allowed into list's
overly-mappingproxied namespace.

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/

Re: [Python-ideas] get() method for list and tuples

2017-03-05 Thread Ed Kellett
On Sun, 5 Mar 2017 at 11:27 Paul Moore  wrote:

> On 3 March 2017 at 18:29, Ed Kellett  wrote:
>
> - Which of the existing things (slice + [default], conditional on a slice,
> > conditional on a len() call) do you think is the obvious way to do it?
>
> Write a small wrapper function that implements the functionality
> (however you want, but it doesn't have to be a single expression, so
> you've much more flexibility to make it readable) and then use that.
>

It's hardly a question of readability at that point. A reader is at the
very least going to have to look at the signature of the utility function
in order to be sure about argument order.

> - Are there any examples where list.get would be applicable and not the
> > obviously best way to do it?
>
> I don't understand the question. If you're asking which is better
> between list.get and a custom written function as described above,
>

No. I'm asking: if list.get did exist, are there any cases (compatibility
with old versions aside) where list.get's semantics would be applicable,
but one of the alternatives would be the better choice?

"writing a helper function" is a generally
> useful idiom that works for many, many things, but list.get only
> solves a single problem and every other such problem would need its
> own separate language change.


Custom helper functions can obviously accomplish anything in any language.
If we had to choose between def: and list.get, I'd obviously opt for the
former.

The disadvantage that you have to write
> the helper is trivial, because it's only a few lines of simple code:
>

I don't think the size of a helper function is relevant to how much of a
disadvantage it is. Most built-in list methods are trivial to implement in
Python, but I'm glad not to have to.

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/

Re: [Python-ideas] get() method for list and tuples

2017-03-05 Thread Ed Kellett
On Sat, 4 Mar 2017 at 09:46 Steven D'Aprano  wrote:

On Fri, Mar 03, 2017 at 10:35:18PM +0100, Michel Desmoulin wrote:

> Since the start of the discussion, contesters have been offering
> numerous solutions, all being contextual and with gotchas, none being
> obvious, simple or elegant.

I do not agree with that characterisation.


Which solution do you think is obvious, simple or elegant?


> "There should be one obvious way to do it" right?

But what is "it" here?

...


Any of the alternatives mentioned thus far solve the "too general" problem.
As far as I can see, none of them would even be a contender if list.get
existed, so I think the fact that people have spent time coming up with
them is telling.


In other words, the status quo. Be specific. Show some
code -- its okay if its simplified code, but it should be enough to
demonstrate the *use-case* for this. "I have crappy JSON" is not a
use-case. How is it crappy and how would you use list.get to fix it?


It's crappy because you have a list of things of unknown length (though I'm
inclined to disagree with the "crappy", honestly—this would seem a
perfectly reasonable thing to do if people weren't bizarrely against it). I
haven't written much Python that is not secret for a while, so excuse the
heavy paraphrasing: In my cases it is usually dealing with arguments: some
sort of list has been sent to me down the wire, let's say ["kill", "edk"].
I could write a big fancy dispatcher, but I see that as unwarranted
complexity, so I usually start with something simpler. I end up wanting to
do something like:

target = args[1]
reason = args.get(2, "")

I could equally use list.pop, which seems to be common when solving this
problem with dicts—except list.pop doesn't take a default either, for some
reason I've never quite understood.


This brings us back to the point I made really early on: this *seems*
like an obviously useful method, by analogy with dicts. I agree! It
*seems* useful, so obviously such that one of the first things I added
to my own personal toolbox of helper functions was a sequence get()
function:

def get(sequence, index, default=None):
try:
return sequence[index]
except IndexError:
return default


But then I never used it. "Seems useful" != "is useful", at least in my
experience.


Helper functions suck, in my view. I'm all for a flat function namespace
with a get() that works on anything, but that one doesn't, and "dict.get is
for dicts and get is for sequences" seems ugly.


> Plus Sven already estimated the implementation would not be very hard.

The simplicity of the implementation argues *against* the need for this
to be a built-in. If you really do need this, then why not add a
sequence get() function to your project? Its only five lines!


Why not:
- Python tries very hard to stop you from adding get() to sequences. Mixing
levels of namespacing feelss wrong, to me.
- It's another function for everybody reading your program to have to
remember about.
  - Functions have other costs too, in terms of documentation and testing.
- It's likely incompatible with other copies of the same utility.


As far as I have seen, only one person apart from myself, Kyle
Lahnakoski, has implemented this helper in their own code. And Kyle
says he has talked himself out of supporting this change.

One thing I haven't seen is anyone saying "I am constantly writing and
re-writing this same helper function over and over again! I grepped my
code base and I've recreated this helper in 30 different modules.
Maybe it should be a built-in?" That would be a good argument, but
nobody has made it. Lots of people saying that they desperately need
this method, but apparently most of them don't need it enough to write a
five line helper function to get it. They'd rather wait until they've
migrated all their code to Python 3.7.


The helper function doesn't solve the problem for me. The existing
solutions are, to my mind, ugly and non-obvious, and writing a helper that
is still ugly and non-obvious doesn't make anything better. The place to
solve this problem is in the API.


> So we have one obvious solution to a problem that:
>
> - several professional programmers said they have

I'm not convinced by claims that "I need to fetch arbitrary indexes from
sequences ALL THE TIME, sorry I can't show any examples...


It's hard to show examples because, generally speaking, when one can't do a
thing one does something else instead. I can restructure my programs to
avoid having this problem, or, if I'm in a hurry, I can use one of the many
ugly h^W^Wobvious, simple and elegant solutions, like (args[2:3] + [""])[0].

In general, I remain curious about cases in which list.get could be used
and would not be the preferred solution.

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/

Re: [Python-ideas] get() method for list and tuples

2017-03-03 Thread Ed Kellett
On Fri, 3 Mar 2017 at 17:03 Ethan Furman  wrote:

> On 03/03/2017 08:09 AM, Ed Kellett wrote:
>
> > P.S. all the talk of PEP 463 seems misplaced. That it solves (FSVO
> solve) this problem doesn't mean it should supersede
> > this discussion.
>
> The advantage of PEP 463 is that issues like this would be less pressing,
> and it's much more general purpose.
>

PEP 463 won't solve this problem for me because its solution is as ugly as
the thing it's replacing. Conceptually, I'd even argue that it's uglier.
Also, if you want a general-purpose solution to everything, propose
with-expressions, but that's another discussion. The existence of
general-purpose things doesn't mean specific issues aren't worth talking
about.


> Personally, I don't think `get` belongs on list/tuple for reasons already
> stated.
>

The reasons already stated boil down to "lists aren't dicts so they
shouldn't share methods", which seems ill-advised at best, and "I wouldn't
use this". I'm not convinced that the latter is generally true; I've often
looked for something like a list.get, been frustrated, and used one (chosen
pretty much at random) of the ugly hacks presented in this thread. I'd be
surprised if I'm the only one.

I guess I don't have any hope of convincing people who think there's no
need to ever do this, but I have a couple of questions for the people who
think the existing solutions are fine:

- Which of the existing things (slice + [default], conditional on a slice,
conditional on a len() call) do you think is the obvious way to do it?
- Are there any examples where list.get would be applicable and not the
obviously best way to do it?

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/

Re: [Python-ideas] get() method for list and tuples

2017-03-03 Thread Ed Kellett
On Tue, 28 Feb 2017 at 17:19 David Mertz  wrote:

> On Tue, Feb 28, 2017 at 7:16 AM, Michel Desmoulin <
> desmoulinmic...@gmail.com> wrote:
>
> Le 28/02/2017 à 15:45, Steven D'Aprano a écrit :
> > No you don't. You can use slicing.
> > alist = [1, 2, 3]
> > print(alist[99:100])  # get the item at position 99
>
> No this gives you a list of one item or an empty list.
>
> dict.get('key', default_value) let you get a SCALAR value, OR a default
> value if it doesn't exist.
>
>
> x = (alist[pos:pos+1] or [default_val])[0]
>
>
> How so ? "get the element x or a default value if it doesn't exist" seem
> at the contrary, a very robust approach.
>
>
> Yes, and easily written as above.  What significant advantage would it
> have to spell the above as:
>

I think code like that is convoluted and confusing and I'm surprised to see
anyone at all advocating it.

IMO, the sane thing to compare this with is a conditional expression. There
aren't any spellings of that that aren't ugly either:

>>> stuff[x] if len(stuff) > x else default
>>> stuff[x] if stuff[x:x+1] else default

As for a reasonable use of list.get (or tuple.get), I often end up with
lists of arguments and would like to take values from the list if they
exist or take a default if not. This looks particularly horrible if the
index isn't a variable (so most of the time):

something = args[1] if len(args) > 1 else "cheese"
something_else = args[2] if len(args) > 2 else "eggs"

(you could make it more horrible by using the slicing trick, but I don't
see much point in demonstrating that.)

I don't often want to use dicts and lists in the same code in this way, but
I think the crucial point about the comparison with dicts is that code like
this is simpler and clearer if you do something horrible like this, just to
get .get():

>>> argdict = dict(enumerate(args))

Ed

P.S. all the talk of PEP 463 seems misplaced. That it solves (FSVO solve)
this problem doesn't mean it should supersede this discussion. Personally,
I don't think I'd use except-expressions, but I would use list.get.
___
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/

Re: [Python-ideas] Delayed Execution via Keyword

2017-02-17 Thread Ed Kellett
On Fri, 17 Feb 2017 at 23:14 Joshua Morton 
wrote:

> @ Ed
>
> Its my understanding that d[k] is always d[k], even if d or k or both are
> delayed. On the other hand, `delayed d[k]` would not be, but you would need
> to explicitly state that. I think its worth expanding on the example Joseph
> made.
>
> I think it makes sense to consider this proposal to be `x = delayed
> ` is essentially equivalent to `x = lambda: `, except that
> there will be no need to explicitly call `x()` to get the delayed value,
> instead it will be evaluated the first time its needed, transparently.
> This, for the moment, assumes that this doesn't cause enormous interpreter
> issues, but I don't think it will. That is, there is no "delayed" object
> that is created and called, and so as a user you really won't care if an
> object is "delayed" or not, you'll just use it and it will be there.
>
> Do you understand this proposal differently?
>
> --Josh
>

Chris mentioned something about it being difficult to decide what evaluates
a delayed thing, and what maintains it. This tangent started with my
suggesting that operators should maintain delayed-ness iff all their
operands are delayed, with ., [] and () as exceptions. That is, I'm
suggesting that d[k] always evaluate d and k, but a + b might defer
evaluation if a and b are both delayed already. Roughly, I guess my
rationale is something like "let operators combine multiple delayed-objects
into one, unless that would break things"—and at least by convention,
operators that aren't ., [] or () don't have behaviour that would be broken.

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/

Re: [Python-ideas] Delayed Execution via Keyword

2017-02-17 Thread Ed Kellett
On Fri, 17 Feb 2017 at 21:58 Joshua Morton 
wrote:

> Ed, I'm not seeing this perceived problem either.
>
> if we have
>
> >>> d = delayed {'a': 1, 'b': 2}  #  I'm not sure how this is delayed
> exactly, but sure
> >>> k = delayed string.ascii_lowercase[0]
> >>> d[k]
> 1
>

My problem with this doesn't have to do with subexpressions.

If d[k] for delayed d and k yields delayed d[k], then someone mutates d,
you get an unexpected result. So I'm suggesting that d[k] for delayed d and
k should evaluate d and k, instead.
___
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-17 Thread Ed Kellett
On Fri, 17 Feb 2017 at 21:57 Joseph Jevnik  wrote:

> > You should be able to pass the result to *any* existing code that
> expects a function and sometimes calls it, and the function should be
> called when that happens, rather than evaluated to a delayed object and
> then discarded.
>
> I disagree with this claim because I do not think that you should have
> side effects and delayed execution anywhere near each other.
>

If Python gets delayed execution, it's going to be near side effects.
That's just the reality we live in.


> You only open youself up to a long list of special cases for when and
> where things get evaluated.
>

Not really. With the function call example, as long as x() always evaluates
x (rather than becoming a delayed call to x), we're all good. Remember that
this has nothing to do with the contents of x, which indeed shouldn't use
delays if it cares about side effects—what might be delayed here is the
expression that finds x.
___
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-17 Thread Ed Kellett
On Fri, 17 Feb 2017 at 21:21 Joseph Jevnik  wrote:

> About the "whatever is d[k]" in five minutes comment: If I created an
> explict closure like: `thunk = lambda: d[k]` and then mutated `d` before
> evaluating the closure you would have the same issue. I don't think it is
> that confusing. If you need to know what `d[k]` evaluates to right now then
> the order of evaluation is part of the correctness of your program and you
> need to sequence execution such that `d` is evaluated before creating that
> closure.
>

If you create an explicit closure, sure. With delayed expressions, you
could explicitly delay d[k], too. If you have an existing d and k,
potentially passed to you by somebody else's code, the delayedness of d and
k should not inflict arbitrarily-delayed sequencing on your attempt to find
out what d[k] is now.
___
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-17 Thread Ed Kellett
On Fri, 17 Feb 2017 at 21:18 Joseph Jevnik  wrote:

> There is no existing code that uses delayed execution so we don't need to
> worry about breaking it.
>

I think you're missing the point here. This thing is transparent—that's
sort of the entire point—so you can pass delayed expressions to other
things, and it would be better if they didn't have insane behaviour.


> I think it would be much easier to reason about if forcing an expression
> was always explicit. I am not sure what you mean with the second case; why
> are you delaying a function if you care about the observable side-effect?
>

You don't delay the function, you delay an expression that evaluates to it.
You should be able to pass the result to *any* existing code that expects a
function and sometimes calls it, and the function should be called when
that happens, rather than evaluated to a delayed object and then discarded.
___
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-17 Thread Ed Kellett
On Fri, 17 Feb 2017 at 19:38 Joseph Jevnik  wrote:

> Delayed execution and respecting mutable semantics seems like a nightmare.
> For most indexers we assume hashability which implies immutability, why
> can't we also do that here? Also, why do we need to evaluate callables
> eagerly?
>

Respecting mutability: we just have to always, we don't know if a delayed
thing is hashable until we evaluate it. This thing has implications for
existing code (since delayed objects can get anywhere) so it should be
careful not to do anything too unpredictable, and I think d[k] meaning
"whatever is in d[k] in five minutes' time" is unpredictable. One can
always delay: d[k] if it's wanted.

Evaluate calls: because if you don't, there's no way to say "strictly
evaluate x() for its side effects".
___
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-17 Thread Ed Kellett
I think trying to eager-ify subexpressions is absurdly difficult to do
right, and also a problem that occurs in other places in Python already, so
solving it only for this new thing that might very well go no further is a
bit odd.

I don't think versions that aren't transparent are much use.

> Interesting. Okay. So in effect, these things aren't objects, they're
magic constructs that turn into objects the moment you do anything
with them, even an identity check. That makes sense.

This seems unfortunate. Why not make these things objects that replace
themselves with the evaluated-to object when they're used?

> "this collapses the waveform, that keeps it in a quantum state"

That's a bit of a false dichotomy ;)

I suggest that operators on delayed-objects defer evaluation iff all of
their operands are delayed, with some hopefully-obvious exceptions:
- Function call: delayed_thing() should evaluate delayed_thing
- Attribute and item access should evaluate the container and key: even if
both operands are delayed, in Python we have to assume things are mutable
(especially if we don't know what they are yet), so we can't guarantee that
delaying the lookup is valid.

Just passing something to a function shouldn't collapse it. That'd make
this completely useless.
___
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] A more readable way to nest functions

2017-01-28 Thread Ed Kellett
On Sat, 28 Jan 2017 at 14:27 zmo via Python-ideas 
wrote:

> I agree this would look a bit more elegant. To focus on the feature of
> that operator, instead of how to write it, I'll use XYZ instead of <| in
> this post.


My thoughts exactly :)


> So, considering it's decided that the RHS is in charge of filling up all
> the arguments of the LHS, how to deal with positional and keyword
> arguments without introducing new syntax?
>

My instinct is that we don't need to deal with that; that's what partial
application is for. To be fair, I'd advocate better syntax for that, but
it's another issue.


> anyway, I guess it's pretty safe to assume that if fn_b() returns a
> scalar, it'll be easy to assume it's just a single positional argument.
>
> > print XYZ some_func XYZ another_func("Hello")
>
> [...]
>
> Meaning that the above could also be written as:
>
> print XYZ some_func XYZ another_func XYZ "Hello"


That looks good to me, but I think another_func("Hello") is the better one
to recommend. I think it makes it slightly more obvious what is going on.


> Then the basic operator definition could be done with a dunder
> looking like: [...]


I think the special-casiness here is unfortunate and would cause problems.
a(b()) doesn't randomly pass kwargs to a if b happens to return a certain
kind of thing.
___
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] A more readable way to nest functions

2017-01-28 Thread Ed Kellett
On Fri, 27 Jan 2017 at 21:29 Ethan Furman  wrote:

On 01/27/2017 01:07 PM, Brent Brinkley wrote:
> Suggested structure:
>
>   print() <| some_func() <| another_func("Hello")

My first question is what does this look like when print() and some_func()
have other parameters?  In other words, what would this look like?

 print('hello', name, some_func('whatsit', another_func('good-bye')),
sep=' .-. ')


This idea doesn't solve the general problem well, but I'm not convinced
that it needs to; that can be addressed by making partial function
application syntax nicer. Although I think it's probably fairly useful
anyway.

FWIW, I'd spell it without the (), so it's simply a right-associative
binary operator on expressions, (a -> b, a) -> b, rather than magic syntax.

print XYZ some_func XYZ another_func("Hello")
___
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] pathlib suggestions

2017-01-25 Thread Ed Kellett
On Wed, 25 Jan 2017 at 05:26 Stephen J. Turnbull <
turnbull.stephen...@u.tsukuba.ac.jp> wrote:

> -1  I don't see how this is an improvement.  If it would raise if
> exist_ok == False, then
>
> try:
> p.rename(another_p, exist_ok=False)
> except ExistNotOKError:
> take_evasive_action(p)
>
> doesn't seem like a big improvement over
>
> if p.exists():
> take_evasive_action(p)
> else:
> p.rename(another_p)
>
> And if it doesn't raise, then the action just silently fails?
>

The latter should be if another_p.exists(), and it can race with the
creation of another_p—this is a textbook motivating example for EAFP.
___
Python-ideas mailing list
Python-ideas@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/