Skip to content
Merged
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 48 additions & 15 deletions pep-0677.rst
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ Motivation
==========

One way to make code safer and easier to analyze is by making sure
that functions and classes are well-typed. In Python we have type
annotations defined by PEP 484 to provide type hints that can find
bugs as well as helping with editor tooling like tab completion,
static analysis tooling, and code review.
that functions and classes are well-typed. In Python we have type
annotations, the framework for which is defined in PEP 484, to provide
type hints that can find bugs as well as helping with editor tooling
like tab completion, static analysis tooling, and code review.

Empirically `we have found
<https://github.com/pradeep90/annotation_collector#typed-projects---callable-type>`_
Expand All @@ -51,28 +51,31 @@ For example, consider the following::
return out


def f0(x) -> int:
def wrap(x: int) -> list[int]:
return [x]

def f1(x, y) -> int:
return y + z
def add(x: int, y: int) -> int:
return x + y

flat_map(f0, [1, 2, 3]) # no runtime error, output is [1, 2, 3]
flat_map(f1, [1, 2, 3]) # runtime error!
flat_map(wrap, [1, 2, 3]) # no runtime error, output is [1, 2, 3]
flat_map(add, [1, 2, 3]) # runtime error: `add` expects 2 arguments, got 1


We can add types to this example to detect the runtime error::

from typing import Callable

def flat_map(l: list[int], func: Callable[[int], list[int]]):
def flat_map(
l: list[int],
func: Callable[[int], list[int]]
) -> list[int]:
....

...


flat_map(f0, [1, 2, 3]) # type checks okay, output is [1, 2, 3]
flat_map(f1, [1, 2, 3]) # type check error
flat_map(wrap, [1, 2, 3]) # type checks okay, output is [1, 2, 3]
flat_map(add, [1, 2, 3]) # type check error

There are a few usability challenges with ``Callable`` we can see here:

Expand All @@ -92,13 +95,16 @@ and hides the error::

from typing import Callable

def flat_map(l: list[int], func: Callable[..., Any]):
def flat_map(
l: list[int],
func: Callable[..., Any]
) -> list[int]:
....

...


flat_map(f1, [1, 2, 3]) # no type check error!
flat_map(add, [1, 2, 3]) # oops, no type check error!


With our proposal, the example looks like this::
Expand Down Expand Up @@ -314,6 +320,34 @@ provided - any function implementing the type must use the same names.
This is similar to the extended syntax proposal we described in our
`Rejected Alternatives`_ section.

Function Definition vs Callable Type Annotations
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In all of the languages listed above, type annotations for function
definitions use a ``:`` rather than a ``->``. For example, in TypeScript
a simple add function looks like this::

function higher_order(fn: (a: string) => string): string {
return fn("Hello, World");
}

Scala and Kotlin use essentially the same ``:`` syntax for return
annotations. The ``:`` makes sense in these languages because they
all use all of these languages use ``:`` for type annotations of
Comment thread
stroxler marked this conversation as resolved.
Outdated
parameters and variables, and the use for function return types is
similar.

In Python, because ``:`` denotes the start of a function body we use a
different token ``->`` for return annotations, which means that the context
for our proposal differs from the other languages in that there's potential
for more confusion when reading function definitions that include callable
types.
Comment thread
stroxler marked this conversation as resolved.
Outdated

This is a key concern for which we are seeking feedback with our draft
PEP; one idea we have floated is to use ``=>`` instead to make it easier
to differentiate.


The ML Language Family
~~~~~~~~~~~~~~~~~~~~~~

Expand All @@ -333,7 +367,6 @@ sense for languages in this family because they use automatic
which means that a multi-argument function behaves like a single-argument
function returning a function.


Specification
=============

Expand Down