[Python-ideas] Language proposal: variable assignment in functional context

Joao S. O. Bueno jsbueno at python.org.br
Wed Jun 21 05:29:10 EDT 2017


I could emulate the "where" semantics as described by Steven using the
class statement and eval :

I guess it is useful if someone want to try refactoring a piece of "real
world"  code, and see
if it really feels better - then we could try to push for the "where"
syntax, which I kinda like:

(This emulation requires the final expression to be either a string to be
eval'ed,
or a lambda function - but then some namespace retrieval and parameter
matching
would require a bit more code on the metaclass):



class Where(type):
   def __new__(metacls, name, bases, namespace, *, expr=''):
       return eval(expr, namespace)

def sqroot(n):
   class roots(expr="[(-b + r)/ (2 * a)  for r in (+ delta **0.5, - delta
** 0.5) ]", metaclass=Where):
       a, b, c = n
       delta = b ** 2 - 4 * a * c

   return roots



On 21 June 2017 at 10:31, Brice PARENT <contact at brice.xyz> wrote:

> I might not have understood it completely, but I think the use cases would
> probably better be splitted into two categories, each with a simple
> solution (simple in usage at least):
>
> *When we just want a tiny scope for a variable:*
>
> Syntax:
>
> with [assignment]:
>     # use of the variable
>
> # assigned variable is now out of scope
>
> Examples:
>
> with b = a + 1:
>     y = b + 2
>
> # we can use y here, but not b
>
> or
>
> with delta = lambda a, b, c: b**2 - 4 * a * c:
>     x1 = (- b - math.sqrt(delta(a, b, c))) / (2 * a)
>     x2 = (- b + math.sqrt(delta(a, b, c))) / (2 * a)
>
> # delta func is now out of scope and has been destroyed
>
> We don't keep unnecessarily some variables, as well as we don't risk any
> collision with outer scopes (and we preserve readability by not declaring a
> function for that).
>
> It would probably mean the assignment operator should behave differently
> than it does now which could have unexpected (to me) implications. It would
> have to support both __enter__ and __exit__ methods, but I don't know if
> this makes any sense. I don't know if with a + 1 as b: would make a
> better sense or be a side-effect or special cases hell.
>
> *When we want to simplify a comprehension:*
>
> (although it would probably help in many other situations)
>
> Syntax:
>
> prepare_iterable(sequence, *functions)
>
> which creates a new iterable containing tuples like (element,
> return_of_function_1, return_of_function_2, ...)
>
> Examples:
>
> [m(spam)[eggs] for _, m in prepare_iterable(sequence, lambda obj: obj[0].field.method) if m]
>
> or, outside of a comprehension:
>
> sequence = [0, 1, 5]
> prepare_iterable(sequence, lambda o: o * 3, lambda o: o + 1)
> # -> [(0, 0, 1), (1, 3, 2), (5, 15, 6)]
> The "prepare_iterable" method name might or might not be the right word
> to use. But English not being my mother language, I'm not the right person
> to discuss this...
> It would be a function instead of a method shared by all iterables to be
> able to yield the elements instead of processing the hole set of data right
> from the start.
> This function should probably belong to the standard library but probably
> not in the general namespace.
>
> -- Brice
>
> Le 17/06/17 à 12:27, Steven D'Aprano a écrit :
>
> On Sat, Jun 17, 2017 at 09:03:54AM +0200, Sven R. Kunze wrote:
>
> On 17.06.2017 02:27, Steven D'Aprano wrote:
>
> I think this is somewhat similar to a suggestion of Nick Coghlan's. One
> possible syntax as a statement might be:
>
> y = b + 2 given:
>     b = a + 1
>
> Just to get this right:this proposal is about reversing the order of
> chaining expressions?
>
> Partly. Did you read the PEP?
> https://www.python.org/dev/peps/pep-3150/
>
> I quote:
>
>     The primary motivation is to enable a more declarative style of
>     programming, where the operation to be performed is presented to the
>     reader first, and the details of the necessary subcalculations are
>     presented in the following indented suite.
>     [...]
>     A secondary motivation is to simplify interim calculations in module
>     and class level code without polluting the resulting namespaces.
>
> It is not *just* about reversing the order, it is also about avoiding
> polluting the current namespace (global, or class) with unnecessary
> temporary variables. This puts the emphasis on the important part of the
> expression, not the temporary/implementation variables:
>
>     page = header + body + footer where:
>         header = ...
>         body = ...
>         footer = ...
>
> There is prior art: the "where" and "let" clauses in Haskell, as well as
> mathematics, where it is very common to defer the definition of
> temporary variables until after they are used.
>
>
>
> Instead of:
>
> b = a + 1
> c = b + 2
>
> we could write it in reverse order:
>
> c = b + 2 given/for:
>     b = a + 1
>
>
> Right. But of course such a trivial example doesn't demonstrate any
> benefit. This might be a better example.
>
> Imagine you have this code, where the regular expression and the custom
> sort function are used in one place only. Because they're only used
> *once*, we don't really need them to be top-level global names, but
> currently we have little choice.
>
> regex = re.compile(r'.*?(\d*).*')
>
> def custom_sort(string):
>     mo = regex.match(string)
>     ... some implementation
>     return key
>
> # Later
> results = sorted(some_strings, key=custom_sort)
>
> # Optional
> del custom_sort, regex
>
>
> Here we get the order of definitions backwards: the thing we actually
> care about, results = sorted(...), is defined last, and mere
> implementation details are given priority as top-level names that
> either hang around forever, or need to be explicitly deleted.
>
> Some sort of "where" clause could allow:
>
> results = sorted(some_strings, key=custom_sort) where:
>     regex = re.compile(r'.*?(\d*).*')
>
>     def custom_sort(string):
>         mo = regex.match(string)
>         ... some implementation
>         return key
>
>
> If this syntax was introduced, editors would soon allow you to fold the
> "where" block and hide it. The custom_sort and regex names would be
> local to the where block and the results = ... line.
>
> Another important use-case is comprehensions, where we often have to
> repeat ourselves:
>
> [obj[0].field.method(spam)[eggs] for obj in sequence if obj[0].field.method]
>
> One work around:
>
> [m(spam)[eggs] for m in [obj[0].field.method for obj in sequence] if m]
>
> But perhaps we could do something like:
>
> [m(spam)[eggs] for obj in sequence where m = obj[0].field.method if m]
>
> or something similar.
>
>
>
>
> If so, I don't know if it just complicates the language with a feature
> which does not save writing nor reading
>
> It helps to save reading, by pushing less-important implementation
> details of an expression into an inner block where it is easy to ignore
> them. Even if you don't have an editor which does code folding, it is
> easy to skip over an indented block and just read the header line,
> ignoring the implementation. We already do this with classes, functions,
> even loops:
>
>     class K:
>         ... implementation of K
>
>     def func(arg):
>         ... implementation of func
>
>     for x in seq:
>         ... implementation of loop body
>
>     page = header + body + footer where:
>         ... implementation of page
>
>
> As a general rule, any two lines at the same level of indentation are
> read as being of equal importance. When we care about the implementation
> details, we "drop down" into the lower indentation block. But when
> skimming the code for a high-level overview, we skip the details of
> indented blocks and focus only on the current level:
>
>     class K:
>     def func(arg):
>     for x in seq:
>     page = header + body + footer where:
>
>
> (That's why editors often provide code folding, to hide the details of
> an indented block. But even without that feature, we can do it in our
> own head, although not as effectively.)
>
>
>
>
>
>
> _______________________________________________
> Python-ideas mailing list
> Python-ideas at python.org
> https://mail.python.org/mailman/listinfo/python-ideas
> Code of Conduct: http://python.org/psf/codeofconduct/
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-ideas/attachments/20170621/8245c499/attachment.html>


More information about the Python-ideas mailing list