Skip to content

Allow yielding result of function that returns None #1933

Open
@gvanrossum

Description

@gvanrossum

UPDATE: see below for current status #1933 (comment)

This is a spin-off from PR #1808. @ddfisher and I cannot agree on what the default type should be in the three methods get_generator_yield_type(), get_generator_receive_type(), get_generator_return_type().

The setting: when the user defines a generator function, they can specify a return type. The return type is required to be a supertype of Generator; this constrains it to Iterator, Iterable, object (and Any, which is special in other ways).

The Generator type has three type parameters:

  • ty, the yield type: the type of y in yield y
  • tc, the receive type: the type of c in c = yield ...
  • tr, the return type: the type of r in return r

Note that tr is fairly new -- it was introduced in Python 3.3 by PEP 380 for the benefit of yield from. tc is older, it dates back to Python 2.5 and PEP 342 (it is the value sent to the generator using the .send() method). ty is the oldest; when yield was originally introduced in Python 2.2 by PEP 255, it was a statement. Before PEP 380, return in a generator function was not allowed to return a value.

When a generator function is just intended to be used as an iterator, there's no need to specify tc and tr, and the recommended type is Iterator[ty]. (Iterable[ty] is also allowed and means roughly the same thing.) In this case the user of the iterator has no access to the .send() method. Hence, yield might as well be considered a statement, since it's never going to return anything other than None. Likewise, there's no point is returning a value with a return statement since the user of the iterator in all likelihood is just going to iterate over it until it raises StopIteration (since that's all that regular iterators do).

This is where @ddfisher and I disagree -- I find it useful if the defaults for tc and tr are None (or perhaps Void), so that mypy will flag an error in the generator body if you either try to use yield ... as an expression, or try to return a value using return <expr>. David (IIUC) believes that both should be Any, giving the reason that "it should be allow to pointless but harmless things." (The code currently has tc default to Void and tr default to Any. Go figure.)

My problem with David's position is that I want mypy to be strict about pointless things that are likely the result of either a misunderstanding or a refactoring gone bad. I have seen enough people make the mistake of accidentally writing return v instead of yield v in a generator that I want this to be flagged as an error. There's also the somewhat analogous case of a regular function declared to return None -- it would be pointless but harmless to return some value here, so maybe David believes that we shouldn't flag that as an error either?

I'm not sure why the existing code (written by David) has Void as the default for tc, but I think it may be because this parameter of Generator is contravariant. So hopefully we at least agree here. :-)

David, please feel free to explain your position more fully (I think you were calling on the formal type system but we ran out of time in our in-person discussion at that point).

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions