I really appreciate all the feedback and all of the thought put into this
idea. I wanted to make a couple of comments on some of the responses and
provide my current thoughts on the idea.

--- Responses to comments ---

> *All* others?
>
> Tuple, frozenset, bytes, bytearray, memoryview, enumerate, range, map, zip,
reversed and filter suggest otherwise.

Yes. You are right. My use of "all" was technically incorrect. But I think
it is *very* disingenuous to pretend that these types play anywhere near as
central a role in python use as list, dict, and set... especially for
newbies. Please try to provide contentful comments instead of "gotchas".

> If we were re-doing Python from scratch, there's a good chance that we
would limit ourselves to a single comprehension syntax, namely generators:
>
>
>     list(expression for x in items if cond)
>     set(expression for x in items if cond)
>     dict((key, value) for x in items if cond)
>
>  rather than have dedicated syntax for those three cases.

This is a very very helpful point. I will address it at the end.

> The proposed syntax doesn't introduce any new concept and would simply
make strings more consistent with the rest of the builtins.  The argument
that we can already do this with the "".join() idiom is backwards. It's
something we have to do _because_ there's no way to write a string
comprehensions directly.

This is the mindset that I had. I understand there are other ways to do
what I am asking. (I provided one in my initial post.) I am saying it
relies on what I believe to be a notoriously unintuitive method (str.join)
and an even more unintuitive way of calling it ("".join).

> Quite literally the ONLY predicate that can be expressed about a single
character is it being a member of a subset of all Unicode characters. Yes,
you could express that in convoluted ways like it's ord() being in a
certain range, but it boils down to subset membership.

I understand if my initial examples led you to think this because I only
iterated over a string "old" to construct "new", but consider the
following. (I know it is a silly example but I'm just trying to get the
point across.)

>>> my_list = ["hotel", "echo", "lima" , "lima", "oscar"]
>>> new = c"x[0] for x in my_list"
>>> new
'hello'

> Rather than toy examples, how about scouring the Python standard library
for some real examples?

Here are 73 of them that I found by grepping through Lib.

   -
   https://github.com/python/cpython/blob/master/Lib/email/_encoded_words.py#L90
   -
   
https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser.py#L126
   -
   
https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser.py#L134
   -
   
https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser.py#L256
   -
   
https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser.py#L260
   -
   
https://github.com/python/cpython/blob/master/Lib/email/_header_value_parser.py#L283
   -
   
https://github.com/python/cpython/blob/master/Lib/lib2to3/fixes/fix_import.py#L29
   -
   
https://github.com/python/cpython/blob/master/Lib/lib2to3/fixes/fix_next.py#L69
   -
   https://github.com/python/cpython/blob/master/Lib/lib2to3/refactor.py#L235
   -
   https://github.com/python/cpython/blob/master/Lib/msilib/__init__.py#L178
   -
   https://github.com/python/cpython/blob/master/Lib/msilib/__init__.py#L290
   -
   
https://github.com/python/cpython/blob/master/Lib/test/_test_multiprocessing.py#L4680
   -
   
https://github.com/python/cpython/blob/master/Lib/test/multibytecodec_support.py#L309
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_audioop.py#L6
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_buffer.py#L853
   -
   
https://github.com/python/cpython/blob/master/Lib/test/test_code_module.py#L123
   -
   
https://github.com/python/cpython/blob/master/Lib/test/test_code_module.py#L139
   -
   
https://github.com/python/cpython/blob/master/Lib/test/test_codeccallbacks.py#L515
   -
   
https://github.com/python/cpython/blob/master/Lib/test/test_codeccallbacks.py#L521
   -
   
https://github.com/python/cpython/blob/master/Lib/test/test_codeccallbacks.py#L824
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L149
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1544
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1548
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1552
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1556
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1953
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_codecs.py#L1991
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_decimal.py#L1092
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_decimal.py#L5346
   -
   
https://github.com/python/cpython/blob/master/Lib/test/test_email/test_email.py#L3526
   -
   
https://github.com/python/cpython/blob/master/Lib/test/test_email/test_email.py#L3535
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_fileinput.py#L91
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_fileinput.py#L92
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_fileinput.py#L93
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_fileinput.py#L94
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L360
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L366
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L372
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L378
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L384
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L391
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L397
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L403
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L409
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L415
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L421
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L427
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L433
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L455
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L457
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L459
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_gettext.py#L461
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_long.py#L305
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_lzma.py#L1049
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_lzma.py#L1087
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L914
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L921
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L927
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L933
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_random.py#L968
   - https://github.com/python/cpython/blob/master/Lib/test/test_re.py#L1013
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_strtod.py#L226
   - https://github.com/python/cpython/blob/master/Lib/test/test_ucn.py#L192
   - https://github.com/python/cpython/blob/master/Lib/test/test_ucn.py#L65
   -
   
https://github.com/python/cpython/blob/master/Lib/test/test_unicodedata.py#L327
   -
   https://github.com/python/cpython/blob/master/Lib/test/test_zipfile.py#L1833
   -
   https://github.com/python/cpython/blob/master/Lib/tkinter/__init__.py#L268
   -
   
https://github.com/python/cpython/blob/master/Lib/unittest/test/test_assertions.py#L178
   -
   
https://github.com/python/cpython/blob/master/Lib/unittest/test/test_case.py#L704
   - https://github.com/python/cpython/blob/master/Lib/urllib/parse.py#L907
   -
   
https://github.com/python/cpython/blob/master/Lib/xml/etree/ElementTree.py#L1986


> To me, the chosen syntax is problematic.  The idea of introducing
structural logic by using “” seems likely to cause confusion. Across all
languages I use, quotes are generally and almost always used to introduce
constant values.  Sometimes, maybe, there are macro related things that may
use quoting, but as a developer, if I see quotes, I’m thinking: the runtime
will treat this as a constant.

I think this is an over-simplification of the quotations syntax.
Python has several prefix characters that you have to look out for when you
see quotes, namely the following: r, u, f, fr, rf, b, br, rb.
Not only can these change the construction syntax, but they can even
construct an object of a completely different type (bytes).

> Second, f-strings do not restrict the normal usage of strings for
freeform text content (apart from making the curly brace characters
special).

Not to nit-pick too much, but the following is a valid string but not a
valid f-string.

>>> s = f"This is a valid string but invalid f-string {}"
  File "<stdin>", line 1
    s = f"This is a valid string but invalid f-string {}"
                                                         ^
SyntaxError: f-string: empty expression not allowed
>>>

> Your proposal is focusing on strings as iterables and drawing a parallel
with other kinds of iterables for which we have comprehensions. But strings
aren't like other iterables because they're primarily vessels for freeform
text content, not structured data.

I view this as the strongest opposition to the idea in the whole thread,
but I think that seal was broken with f-strings and the {}-syntax. The
proposed syntax is different from those features only in *degree* (of
deviation from strict char-arrays) not in *type*. But I also recognize that
the delimiters {} go a long way in helping to mentally compartmentalize
chars from python code.

--- My current thoughts ---

I definitely see the drawbacks of the originally-proposed syntax, but I
think it would be beneficial to the conversation for commenters to
recognize that *python strings are not nearly as pure as some of the
objections make them out to be*. I would be happy to hear the objection
that my syntax strays *too* far, but many of the passed-around examples
attest to the fact that when users see quotes, they are often already in
"code-evaluation" mode (eg. f"[{','.join('0123')}]" ).

I think that a comment left by steve was particularly helpful.

> If we were re-doing Python from scratch, there's a good chance that we
would limit ourselves to a single comprehension syntax, namely generators:
>
>
>     list(expression for x in items if cond)
>     set(expression for x in items if cond)
>     dict((key, value) for x in items if cond)
>
>  rather than have dedicated syntax for those three cases.

Would readers see any merit in a syntax like the following?

>>> dirty = "f8sjGe7"
>>> clean = str(char for char in dirty if char in string.ascii_letters)
>>> clean
'fsjGe'

Or would it stray too far from the behavior of the str() constructor
in general?

As of now, the behavior is the following.

>>> dirty = "f8sjGe7"
>>> clean = str(char for char in dirty if char in string.ascii_letters)
>>> clean
'<generator object <genexpr> at 0x7f10fc917660>'

*I don't intend to reinvent strings, I only mean to leverage an already
existing means of signifying modified string construction syntax (prefixes)
to align str construction syntax with the comprehensions available for the
other most common builtin iterables, avoid the notoriously unintuitive
"".join syntax, and improve readability.*

Please continue to send your thoughts! I really appreciate it!

DQAL


On Sun, May 2, 2021 at 3:51 AM Stephen J. Turnbull <
turnbull.stephen...@u.tsukuba.ac.jp> wrote:

> Valentin Berlier writes:
>
>  >     f"""
>  >     Guest list ({len(people)} people):
>  >     {person.name + '\n' for person in people}
>  >     """
>
> That's nice!  It's already (almost[1]) legal syntax, but it prints the
> repr of the generator function.  This could work, though:
>
>     f"""
>     Guest list ({len(people)} people):
>     {person.name + chr(10) for person in people:5.25i}
>     """
>
> with i for "iterate iterable".  (The iterable might need to be
> parenthesized if it's a generator function.)  The width spec is
> intended to be max_elems.per_elem_width.  I guess you could also
> generalize it to something like
>
>     f"""
>     Guest list ({len(people)} people):
>     {person.name, '>25s', chr(10), '' for person in people:i}
>     """
>
> where the 2d element of the tuple is a format spec to apply to each
> element, the 3d is the separator and the 4th the terminator.  Or
> perhaps those parameters belong in the syntax of the 'i' format code.
>
> I saw your later post that suggests making this default.  We could
> tell programmers to use !s or !r if they want to see things like
>
>     <generator object <genexpr> at 0x100fde580>
>
> Probably not, though, at least not if you want all iterables treated
> this way.  Possibly this could be restricted to generators, especially
> if you use the element format as tuple syntax I proposed above rather
> than embed the element format spec in the overall generator spec.
>
> Steve
>
> Footnotes:
> [1]  Need to substitute 'chr(10)' for '\n' in an f-string.
>
> _______________________________________________
> Python-ideas mailing list -- python-ideas@python.org
> To unsubscribe send an email to python-ideas-le...@python.org
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-ideas@python.org/message/YG4JTKKQSJO2X3EQH7KJCJEGQ44OSXNC/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
Python-ideas mailing list -- python-ideas@python.org
To unsubscribe send an email to python-ideas-le...@python.org
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/python-ideas@python.org/message/JLJT4YNBBIVL27H4NSFLJWMQ3ISFB6WI/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to