Regarding the discussion about using the tilde operator (an idea which has
grown on me a bit):

Would it help, for the purpose of alerting the user to potential backwards
compatibility issues, to add a *second* dunder method, __optional__, or
perhaps __tilde__. to  the tilde operator resolution order (is that the
correct phrase...?) that applies to `type` objects only?

Possible additional benefit:

   - A FooMcls.__optional__ method could provide ability to use
   typing.Optional to type against a custom MissingType
      - example: if Foo's metaclass is defined such that Optional[Foo]
      produces Union[Foo, MissingType]:

class MissingType: ...

MISSING = MissingType()

def func(x: Optional[Foo] = MISSING): ...


   - The Optional type would need to be changed to look for the
   __optional__ method at mpy runtime

The logic could be like so:

   - when ~foo is invoked...
      - if foo has an __invert__ method, tilde always uses that; this will
      preserve compatibility for objects
      - if foo is a type, use the __optional__ method, if it is present
      (this step is skipped for non-type objects)
      - if a type object provides *both*, when used in the context of
      typing, provide a warning to the user that the Foo type object
is returning
      a value from __invert__, which is not as expected

---
Ricky.

"I've never met a Kentucky man who wasn't either thinking about going home
or actually going home." - Happy Chandler


On Tue, Sep 3, 2019 at 11:42 AM Philippe Prados <[email protected]> wrote:

> Hello,
>
> I propose to resume all the arguments (I add my remarks in italic) and
> major questions.
>
> Add a new operator for `Union[type1|type2]` ?
>
>    -
>
>    CONS: This is not a new proposal. If I recall correctly, it was
>    proposed way back at the very beginning of the type-hinting discussion, and
>    there has been at least one closed feature request for it:
>    https://github.com/python/typing/issues/387
>    -
>
>       It is maybe too late to change this, many people are already get
>       used to current notation.
>       -
>
>          I propose to add a new notation, not to replace the notation
>          -
>
>       This syntax is difficult to google, if someone encounters it in code
>       -
>
>       It is still not possible to use `|` for unions because of built-in
>       types. (This would require a corresponding slot in type which is a
>       non-starter)
>       -
>
>          I do it
>          -
>
>       There are currently no volunteer to implement this in mypy
>       -
>
>          I implement this (One patch for CPython
>          <https://github.com/pprados/cpython> and one for MyPy
>          <https://github.com/pprados/mypy>).
>          -
>
>       “but as @ilevkivskyi pointed out, that is not an option (at least
>       until Python 4).”
>       -
>
>          Is it time now ?
>          -
>
>    PRO: It’s similar of Scala
>    <https://dotty.epfl.ch/docs/reference/new-types/union-types.html>
>    -
>
>    PRO: Seems like `foo | None` is just as readable
>    -
>
>    PRO: Which means you couldn’t use this feature in Python 3.7, much
>    less 2.7. I’m not sure it maintaining backward compatibility in typing and
>    in mypy is still as important today as it was 5 years ago, but I’m pretty
>    sure it hasn’t been abandoned entirely.
>    -
>
>    CONS: add operator introducing a dependency to typing in builtins
>    -
>
>    CONS:  supporting this would likely break compatibility with existing
>    code that overloads `|` for class objects using a metaclass. We could
>    perhaps work around this by making `|` inside an annotation context
>    different from the regular `|` operator.
>    -
>
>       A work around is to use `Union[type1,type2]` in this case
>       -
>
>    CONS: as breaking the backport (in that typing.py can easily be
>    backported but core `types` can't)
>    -
>
>       There are several things in the typing syntax that require a
>       certain minimum version. E.g. type annotations require Python 3 (whereas
>       type comments work in Python 2 too), type annotations on variables (PEP
>       526) require 3.6+, `from __future__ import annotations` (PEP 563)
>       requires 3.7+.
>       -
>
>    PRO: I mean that at run-time, `int|str` might return a very simple
>    object in 3.9, rather than everything that you'd need to grab from
>    importing `typing`. Wondering if doing so would close off the
>    possibility of, in 3.12 or something, making it a more directly usable
>    "type union" that has other value.
>    -
>
>    CONS: if Python itself doesn't have to be changed, we'd still need to
>    implement it in mypy, Pyre, PyCharm, Pytype, and who knows what else.
>    -
>
>       My patch of mypy is just 20 lines of codes
>
> If yes,
>
>    -
>
>    Change only the PEP484 <https://www.python.org/dev/peps/pep-0484/>
>    (Type hints) to accept the syntax `type1 | type2` ?
>    -
>
>       PRO: The PEP563 <https://www.python.org/dev/peps/pep-0563/>
>       (Postponed Evaluation of Annotations) is enough to accept this 
> proposition
>       -
>
>          CONS: The Resolving type hints at runtime
>          
> <https://www.python.org/dev/peps/pep-0563/#resolving-type-hints-at-runtime>
>          says: “For code which uses annotations for other purposes, a
>          regular eval(ann, globals, locals) call is enough to resolve the 
> annotation.”.
>          Without add a new operator `__or__` in type `type`, it’s not
>          possible to resolve type hints at runtime.
>
> >>> from __future__ import annotations
> >>> def foo() -> int | str: pass
> ...
> >>> eval(foo.__annotations__['return'])
> Traceback (most recent call last):
>   File "<stdin>", line 1, in <module>
>   File "<string>", line 1, in <module>
> TypeError: unsupported operand type(s) for |: 'type' and 'type'
>
>    -
>
>    CONS: Without operator, it’s not possible to write
>
> >>> u = int | str
> >>> u
> typing.Union[int, str]
>
>
>    -
>
>    Use `(int, str)` in place of `Union[int,str]` ?
>    -
>
>       PRO: This doesn't have compatibility issues and it's similar to 
> `isinstance(foo,
>       (int, str))`
>       -
>
>       PRO: Either better than breaking backward compatibility by adding
>       new operator methods to the type `type`.
>       -
>
>       CONS: In most languages with similar-ish type syntax, `(int, str)`
>       means `Tuple[int, str]`, not `Union[int, str]`.
>       -
>
>    Use `{int, str}` in place of `Union[int,str]` ?
>    -
>
>       PRO: big advantage of `{int, str}` over `int|str`. It doesn't
>       require adding anything to `type`, and we don't need to introduce a
>       new lightweight builtin union type.
>
> Add a new operator for `Optional[type]` ?
>
>    -
>
>    CONS: `foo | None` is short and readable
>    -
>
>    CONS: `foo | None` it's 3 fewer characters than `Optional[foo]`, or 30
>    fewer if you include the full removal of `from typing import Optional`.
>    the additional gain of `~foo` is only 6 characters.
>    -
>
>    PRO: help the readability, with a lot of parameters:
>
> def f(source: str | None, destination: str | None, param: int | None):...
> def f(source: ~str, destination: ~str, param: ~int):...
>
>    -
>
>    PRO: I'm currently working on annotating a very large codebase, and
>    `Optional[T]` is so frequent that I think `T | None` would not be
>    enough of an improvement.
>    -
>
>    PRO: Adding a default `__or__` overload to `type` seems a reasonable
>    price to pay in 3.9, and ditto for `__invert__`. Type checkers can
>    support this in older Python versions using PEP 563 or in type comments or
>    in "forward references" (types hidden in string literals).
>    -
>
>    CONS: The `~` is easy to be missed (at least by human readers) and the
>    meaning not obvious.
>    -
>
>    PRO: Also, Python’s typing system is a lot easier to grasp if you’re
>    familiar with an established modern-typed language (Swift, Scala, Haskell,
>    F#, etc.), and they also use `Optional[T]` (or `optional<T>` or `Maybe
>    t` or some other spelling of the same idea) all over be place—so often
>    that many of them have added shortcuts like `T?` to make it easier to
>    write and less intrusive to read.
>
> if yes,
>
> Add operator `__revert__` in type type to use syntax like `~int` ?
>
>    -
>
>    CONS: `~` is not automatically readable
>    -
>
>       like `:` to separate variable and typing.
>       -
>
>    CONS: `~` means complement, which is a completely different thing from
>    `|None`. `~int` seems like it would actually harm comprehension
>    instead of helping.
>    -
>
>    PRO: the slight abuse of `~int` meaning "maybe int" is pretty
>    plausible (consider how "approximately equal" is written mathematically).
>    -
>
>    PRO: Possibly relevant for tilde:
>    
> https://www.thecut.com/article/why-the-internet-tilde-is-our-most-perfect-tool-for-snark.html
>
>    -
>
>    CONS: With `~` there probably won't be a confusion in that sense, but
>    someone reading it for the first time will definitely need to look it up
>    (which is fine i.m.o.).
>    -
>
>       Like the first time someone reading the annotation
>
> def f(a=int):...
> def f(a:int):...
>
> Add operator __add__ in type type to use syntax like +int ?
>
>    -
>
>    PRO: `+foo` definitely seems to say "foo, plus something else" to me
>    much more than `~foo`.
>    -
>
>    CON: `+foo` is less intuitive than `~foo` for `Optional`
>
> Like Kotlin <https://kotlinlang.org/docs/reference/null-safety.html>, add
> a new `?` operator to use syntax like `int?` ou `?int` ?
>
>    -
>
>    CONS: It’s not compatible with IPython and Jupyter Lab `?smth`
>    displays help for symbol `smth`
>    -
>
>    CONS: With default arguments, `?=` looks... not great
>    -
>
>    def f(source: str?=def_src, destination: str?=MISSING, param: int?=1):
>    ...
>
> Extend `isinstance()` and `issubclass()` to accept `Union` ?
>
> isinstance(x, str | int) ==> "is x an instance of str or int"
>
>    -
>
>    PRO: if they were permitted, then instance checks could use an
>    extremely clean-looking notation for "any of these":
>
> Do nothing, open a new PEP or extend PEP585 ?
>
>    -
>
>    I do think we should probably review PEP 585
>    <https://www.python.org/dev/peps/pep-0585/> before doing anything
>    about unions specifically -- likely there are bigger fish to fry
>    -
>
>    PEP 585 has not received much discussion
>
>
> So, I think it’s time to answer these questions:
>
>    -
>
>    Add a new operator for `Union[type1|type2]` ?
>    -
>
>    If yes
>    -
>
>       Change only the PEP484 (Type hints) to accept the syntax `type1 |
>       type2` ?
>       -
>
>       Use `(int, str)` in place of `Union[int,str]` ?
>       -
>
>       Use `{int, str]` in place of `Union[int,str]` ?
>       -
>
>    Add a new operator for `Optional[type]` ?
>    -
>
>    If yes,
>    -
>
>       Add operator `__revert__` in type type to use syntax like `~int` ?
>       -
>
>       Add operator `__add__` in type type to use syntax like +int ?
>       -
>
>    Extend `isinstance()` and `issubclass()` to accept `Union` ?
>
> Do nothing, open a new PEP
> <https://github.com/pprados/peps/blob/master/pep-9999.rst> or extend
> PEP585 <https://www.python.org/dev/peps/pep-0585/> ?
>
> Philippe
>
> Le mar. 3 sept. 2019 à 08:30, Philippe Prados <[email protected]> a écrit :
>
>> With my implementation, I can check
>> assert int | None == None | int
>> is true
>>
>> Le lun. 2 sept. 2019 à 13:32, Ivan Levkivskyi <[email protected]> a
>> écrit :
>>
>>> On Thu, 29 Aug 2019 at 23:48, Guido van Rossum <[email protected]> wrote:
>>>
>>>> On Thu, Aug 29, 2019 at 3:33 PM Chris Angelico <[email protected]>
>>>> wrote:
>>>>
>>>>> On Fri, Aug 30, 2019 at 8:28 AM Guido van Rossum <[email protected]>
>>>>> wrote:
>>>>
>>>> [...]
>>>>
>>>>
>>>> I do tink we should probably review PEP 585 before doing anything about
>>>> unions specifically -- likely there are bigger fish to fry. (And PEP 585
>>>> has not received much discussion.)
>>>>
>>>
>>> I also agree with this. Generally I am fine with Union[int, str] and
>>> Optional[int], but I also see how some people might want a shorter
>>> notation. Many things around typing have been previously
>>> rejected because we didn't want to introduce any (or at least minimal)
>>> changes to the syntax and runtime, but now that typing is much more widely
>>> used we can reconsider some of these.
>>> Importantly, I think this should be done in a systematic way
>>> (potentially using PEP 585 draft as a starting point).
>>>
>>> --
>>> Ivan
>>>
>>> _______________________________________________
>>> Python-ideas mailing list -- [email protected]
>>> To unsubscribe send an email to [email protected]
>>> https://mail.python.org/mailman3/lists/python-ideas.python.org/
>>> Message archived at
>>> https://mail.python.org/archives/list/[email protected]/message/YZHAERD5T4TZL62A6EI4BCKKAQYNJGSU/
>>> Code of Conduct: http://python.org/psf/codeofconduct/
>>>
>> _______________________________________________
> Python-ideas mailing list -- [email protected]
> To unsubscribe send an email to [email protected]
> https://mail.python.org/mailman3/lists/python-ideas.python.org/
> Message archived at
> https://mail.python.org/archives/list/[email protected]/message/SAHJWREC7ESRH3WTDITRM2JSI3OUMPFM/
> Code of Conduct: http://python.org/psf/codeofconduct/
>
_______________________________________________
Python-ideas mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-ideas.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/KCTOMMFGSLZDA6ZY6MGJM2IJWWTFI2RB/
Code of Conduct: http://python.org/psf/codeofconduct/

Reply via email to