1384 lines
131 KiB
HTML
1384 lines
131 KiB
HTML
|
||
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="color-scheme" content="light dark">
|
||
<title>PEP 677 – Callable Type Syntax | peps.python.org</title>
|
||
<link rel="shortcut icon" href="../_static/py.png">
|
||
<link rel="canonical" href="https://peps.python.org/pep-0677/">
|
||
<link rel="stylesheet" href="../_static/style.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/mq.css" type="text/css">
|
||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" media="(prefers-color-scheme: light)" id="pyg-light">
|
||
<link rel="stylesheet" href="../_static/pygments_dark.css" type="text/css" media="(prefers-color-scheme: dark)" id="pyg-dark">
|
||
<link rel="alternate" type="application/rss+xml" title="Latest PEPs" href="https://peps.python.org/peps.rss">
|
||
<meta property="og:title" content='PEP 677 – Callable Type Syntax | peps.python.org'>
|
||
<meta property="og:description" content="This PEP introduces a concise and friendly syntax for callable types, supporting the same functionality as typing.Callable but with an arrow syntax inspired by the syntax for typed function signatures. This allows types like Callable[[int, str], bool] t...">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="https://peps.python.org/pep-0677/">
|
||
<meta property="og:site_name" content="Python Enhancement Proposals (PEPs)">
|
||
<meta property="og:image" content="https://peps.python.org/_static/og-image.png">
|
||
<meta property="og:image:alt" content="Python PEPs">
|
||
<meta property="og:image:width" content="200">
|
||
<meta property="og:image:height" content="200">
|
||
<meta name="description" content="This PEP introduces a concise and friendly syntax for callable types, supporting the same functionality as typing.Callable but with an arrow syntax inspired by the syntax for typed function signatures. This allows types like Callable[[int, str], bool] t...">
|
||
<meta name="theme-color" content="#3776ab">
|
||
</head>
|
||
<body>
|
||
|
||
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
||
<symbol id="svg-sun-half" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Following system colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="9"></circle>
|
||
<path d="M12 3v18m0-12l4.65-4.65M12 14.3l7.37-7.37M12 19.6l8.85-8.85"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-moon" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected dark colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>
|
||
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z"></path>
|
||
</svg>
|
||
</symbol>
|
||
<symbol id="svg-sun" viewBox="0 0 24 24" pointer-events="all">
|
||
<title>Selected light colour scheme</title>
|
||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none"
|
||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||
<circle cx="12" cy="12" r="5"></circle>
|
||
<line x1="12" y1="1" x2="12" y2="3"></line>
|
||
<line x1="12" y1="21" x2="12" y2="23"></line>
|
||
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
|
||
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
|
||
<line x1="1" y1="12" x2="3" y2="12"></line>
|
||
<line x1="21" y1="12" x2="23" y2="12"></line>
|
||
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
|
||
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
|
||
</svg>
|
||
</symbol>
|
||
</svg>
|
||
<script>
|
||
|
||
document.documentElement.dataset.colour_scheme = localStorage.getItem("colour_scheme") || "auto"
|
||
</script>
|
||
<section id="pep-page-section">
|
||
<header>
|
||
<h1>Python Enhancement Proposals</h1>
|
||
<ul class="breadcrumbs">
|
||
<li><a href="https://www.python.org/" title="The Python Programming Language">Python</a> » </li>
|
||
<li><a href="../pep-0000/">PEP Index</a> » </li>
|
||
<li>PEP 677</li>
|
||
</ul>
|
||
<button id="colour-scheme-cycler" onClick="setColourScheme(nextColourScheme())">
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-dark"><use href="#svg-moon"></use></svg>
|
||
<svg aria-hidden="true" class="colour-scheme-icon-when-light"><use href="#svg-sun"></use></svg>
|
||
<span class="visually-hidden">Toggle light / dark / auto colour theme</span>
|
||
</button>
|
||
</header>
|
||
<article>
|
||
<section id="pep-content">
|
||
<h1 class="page-title">PEP 677 – Callable Type Syntax</h1>
|
||
<dl class="rfc2822 field-list simple">
|
||
<dt class="field-odd">Author<span class="colon">:</span></dt>
|
||
<dd class="field-odd">Steven Troxler <steven.troxler at gmail.com>,
|
||
Pradeep Kumar Srinivasan <gohanpra at gmail.com></dd>
|
||
<dt class="field-even">Sponsor<span class="colon">:</span></dt>
|
||
<dd class="field-even">Guido van Rossum <guido at python.org></dd>
|
||
<dt class="field-odd">Discussions-To<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/">Python-Dev list</a></dd>
|
||
<dt class="field-even">Status<span class="colon">:</span></dt>
|
||
<dd class="field-even"><abbr title="Formally declined and will not be accepted">Rejected</abbr></dd>
|
||
<dt class="field-odd">Type<span class="colon">:</span></dt>
|
||
<dd class="field-odd"><abbr title="Normative PEP with a new feature for Python, implementation change for CPython or interoperability standard for the ecosystem">Standards Track</abbr></dd>
|
||
<dt class="field-even">Topic<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="../topic/typing/">Typing</a></dd>
|
||
<dt class="field-odd">Created<span class="colon">:</span></dt>
|
||
<dd class="field-odd">13-Dec-2021</dd>
|
||
<dt class="field-even">Python-Version<span class="colon">:</span></dt>
|
||
<dd class="field-even">3.11</dd>
|
||
<dt class="field-odd">Post-History<span class="colon">:</span></dt>
|
||
<dd class="field-odd">16-Dec-2021</dd>
|
||
<dt class="field-even">Resolution<span class="colon">:</span></dt>
|
||
<dd class="field-even"><a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/message/NHCLHCU2XCWTBGF732WESMN42YYVKOXB/">Python-Dev message</a></dd>
|
||
</dl>
|
||
<hr class="docutils" />
|
||
<section id="contents">
|
||
<details><summary>Table of Contents</summary><ul class="simple">
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#motivation">Motivation</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
|
||
<li><a class="reference internal" href="#an-arrow-syntax-for-callable-types">An Arrow Syntax for Callable Types</a></li>
|
||
<li><a class="reference internal" href="#compact-syntax-for-paramspec">Compact Syntax for <code class="docutils literal notranslate"><span class="pre">ParamSpec</span></code></a></li>
|
||
<li><a class="reference internal" href="#comparing-to-other-languages">Comparing to Other Languages</a><ul>
|
||
<li><a class="reference internal" href="#id1">TypeScript</a></li>
|
||
<li><a class="reference internal" href="#id3">Kotlin</a></li>
|
||
<li><a class="reference internal" href="#id5">Scala</a></li>
|
||
<li><a class="reference internal" href="#function-definitions-vs-callable-type-annotations">Function Definitions vs Callable Type Annotations</a></li>
|
||
<li><a class="reference internal" href="#the-ml-language-family">The ML Language Family</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#typing-behavior">Typing Behavior</a></li>
|
||
<li><a class="reference internal" href="#grammar-and-ast">Grammar and AST</a></li>
|
||
<li><a class="reference internal" href="#implications-of-the-grammar">Implications of the Grammar</a><ul>
|
||
<li><a class="reference internal" href="#precedence-of">Precedence of -></a></li>
|
||
<li><a class="reference internal" href="#async-keyword"><code class="docutils literal notranslate"><span class="pre">async</span></code> Keyword</a></li>
|
||
<li><a class="reference internal" href="#trailing-commas">Trailing Commas</a></li>
|
||
<li><a class="reference internal" href="#disallowing-as-an-argument-type">Disallowing <code class="docutils literal notranslate"><span class="pre">...</span></code> as an Argument Type</a></li>
|
||
<li><a class="reference internal" href="#incompatibility-with-other-possible-uses-of-and">Incompatibility with other possible uses of <code class="docutils literal notranslate"><span class="pre">*</span></code> and <code class="docutils literal notranslate"><span class="pre">**</span></code></a></li>
|
||
<li><a class="reference internal" href="#compatibility-with-arrow-based-lambda-syntax">Compatibility with Arrow-Based Lambda Syntax</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#runtime-behavior">Runtime Behavior</a><ul>
|
||
<li><a class="reference internal" href="#evaluation-and-structured-api">Evaluation and Structured API</a></li>
|
||
<li><a class="reference internal" href="#backward-compatible-api">Backward-Compatible API</a></li>
|
||
<li><a class="reference internal" href="#additional-behaviors-of-types-callabletype">Additional Behaviors of <code class="docutils literal notranslate"><span class="pre">types.CallableType</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a><ul>
|
||
<li><a class="reference internal" href="#extended-syntax-supporting-named-and-optional-arguments">Extended Syntax Supporting Named and Optional Arguments</a></li>
|
||
<li><a class="reference internal" href="#syntax-closer-to-function-signatures">Syntax Closer to Function Signatures</a></li>
|
||
<li><a class="reference internal" href="#other-proposals-considered">Other Proposals Considered</a><ul>
|
||
<li><a class="reference internal" href="#functions-as-types">Functions-as-Types</a></li>
|
||
<li><a class="reference internal" href="#hybrid-keyword-arrow-syntax">Hybrid keyword-arrow Syntax</a></li>
|
||
<li><a class="reference internal" href="#parenthesis-free-syntax">Parenthesis-Free Syntax</a></li>
|
||
<li><a class="reference internal" href="#requiring-outer-parentheses">Requiring Outer Parentheses</a></li>
|
||
<li><a class="reference internal" href="#making-bind-tighter-than">Making <code class="docutils literal notranslate"><span class="pre">-></span></code> bind tighter than <code class="docutils literal notranslate"><span class="pre">|</span></code></a></li>
|
||
<li><a class="reference internal" href="#introducing-type-strings">Introducing type-strings</a></li>
|
||
<li><a class="reference internal" href="#improving-usability-of-the-indexed-callable-type">Improving Usability of the Indexed Callable Type</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#alternative-runtime-behaviors">Alternative Runtime Behaviors</a><ul>
|
||
<li><a class="reference internal" href="#alternative-apis">Alternative APIs</a></li>
|
||
<li><a class="reference internal" href="#using-the-plain-return-type-in-args-for-async-types">Using the plain return type in <code class="docutils literal notranslate"><span class="pre">__args__</span></code> for async types</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backward-compatibility">Backward Compatibility</a></li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#open-issues">Open Issues</a><ul>
|
||
<li><a class="reference internal" href="#details-of-the-runtime-api">Details of the Runtime API</a></li>
|
||
<li><a class="reference internal" href="#optimizing-syntaxerror-messages">Optimizing <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> messages</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#resources">Resources</a><ul>
|
||
<li><a class="reference internal" href="#background-and-history">Background and History</a></li>
|
||
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
</details></section>
|
||
<section id="abstract">
|
||
<h2><a class="toc-backref" href="#abstract" role="doc-backlink">Abstract</a></h2>
|
||
<p>This PEP introduces a concise and friendly syntax for callable types,
|
||
supporting the same functionality as <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code> but with an
|
||
arrow syntax inspired by the syntax for typed function
|
||
signatures. This allows types like <code class="docutils literal notranslate"><span class="pre">Callable[[int,</span> <span class="pre">str],</span> <span class="pre">bool]</span></code> to
|
||
be written as <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">str)</span> <span class="pre">-></span> <span class="pre">bool</span></code>.</p>
|
||
<p>The proposed syntax supports all the functionality provided by
|
||
<code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code> and <code class="docutils literal notranslate"><span class="pre">typing.Concatenate</span></code>, and is intended to
|
||
work as a drop-in replacement.</p>
|
||
</section>
|
||
<section id="motivation">
|
||
<h2><a class="toc-backref" href="#motivation" role="doc-backlink">Motivation</a></h2>
|
||
<p>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, the framework for which is defined in <a class="pep reference internal" href="../pep-0484/" title="PEP 484 – Type Hints">PEP 484</a>, to provide
|
||
type hints that can find bugs as well as helping with editor tooling
|
||
like tab completion, static analysis tooling, and code review.</p>
|
||
<p>Consider the following untyped code:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">flat_map</span><span class="p">(</span><span class="n">func</span><span class="p">,</span> <span class="n">l</span><span class="p">):</span>
|
||
<span class="n">out</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="n">l</span><span class="p">:</span>
|
||
<span class="n">out</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">func</span><span class="p">(</span><span class="n">element</span><span class="p">))</span>
|
||
<span class="k">return</span> <span class="n">out</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">wrap</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
|
||
<span class="k">return</span> <span class="p">[</span><span class="n">x</span><span class="p">]</span>
|
||
|
||
<span class="k">def</span> <span class="nf">add</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">x</span> <span class="o">+</span> <span class="n">y</span>
|
||
|
||
<span class="n">flat_map</span><span class="p">(</span><span class="n">wrap</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> <span class="c1"># no runtime error, output is [1, 2, 3]</span>
|
||
<span class="n">flat_map</span><span class="p">(</span><span class="n">add</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> <span class="c1"># runtime error: `add` expects 2 arguments, got 1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We can add types to this example to detect the runtime error:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span>
|
||
|
||
<span class="k">def</span> <span class="nf">flat_map</span><span class="p">(</span>
|
||
<span class="n">func</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="nb">int</span><span class="p">],</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]],</span>
|
||
<span class="n">l</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
|
||
<span class="o">....</span>
|
||
|
||
<span class="o">...</span>
|
||
|
||
|
||
<span class="n">flat_map</span><span class="p">(</span><span class="n">wrap</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> <span class="c1"># type checks okay, output is [1, 2, 3]</span>
|
||
<span class="n">flat_map</span><span class="p">(</span><span class="n">add</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> <span class="c1"># type check error</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>There are a few usability challenges with <code class="docutils literal notranslate"><span class="pre">Callable</span></code> we can see here:</p>
|
||
<ul class="simple">
|
||
<li>It is verbose, particularly for more complex function signatures.</li>
|
||
<li>It relies on two levels of nested brackets, unlike any other generic
|
||
type. This can be especially hard to read when some of the type
|
||
parameters are themselves generic types.</li>
|
||
<li>The bracket structure is not visually similar to how function signatures
|
||
are written.</li>
|
||
<li>It requires an explicit import, unlike many of the other most common
|
||
types like <code class="docutils literal notranslate"><span class="pre">list</span></code> and <code class="docutils literal notranslate"><span class="pre">dict</span></code>.</li>
|
||
</ul>
|
||
<p>Possibly as a result, <a class="reference external" href="https://github.com/pradeep90/annotation_collector#typed-projects---callable-type">programmers often fail to write complete
|
||
Callable types</a>.
|
||
Such untyped or partially-typed callable types do not check the
|
||
parameter types or return types of the given callable and thus negate
|
||
the benefits of static typing. For example, they might write this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Callable</span>
|
||
|
||
<span class="k">def</span> <span class="nf">flat_map</span><span class="p">(</span>
|
||
<span class="n">func</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="n">Any</span><span class="p">],</span>
|
||
<span class="n">l</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
|
||
<span class="o">....</span>
|
||
|
||
<span class="o">...</span>
|
||
|
||
|
||
<span class="n">flat_map</span><span class="p">(</span><span class="n">add</span><span class="p">,</span> <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">])</span> <span class="c1"># oops, no type check error!</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>There’s some partial type information here - we at least know that <code class="docutils literal notranslate"><span class="pre">func</span></code>
|
||
needs to be callable. But we’ve dropped too much type information for
|
||
type checkers to find the bug.</p>
|
||
<p>With our proposal, the example looks like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">flat_map</span><span class="p">(</span>
|
||
<span class="n">func</span><span class="p">:</span> <span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">],</span>
|
||
<span class="n">l</span><span class="p">:</span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">list</span><span class="p">[</span><span class="nb">int</span><span class="p">]:</span>
|
||
<span class="n">out</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">for</span> <span class="n">element</span> <span class="ow">in</span> <span class="n">l</span><span class="p">:</span>
|
||
<span class="n">out</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">f</span><span class="p">(</span><span class="n">element</span><span class="p">))</span>
|
||
<span class="k">return</span> <span class="n">out</span>
|
||
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The type <code class="docutils literal notranslate"><span class="pre">(int)</span> <span class="pre">-></span> <span class="pre">list[int]</span></code> is more concise, uses an arrow similar
|
||
to the one indicating a return type in a function header, avoids
|
||
nested brackets, and does not require an import.</p>
|
||
</section>
|
||
<section id="rationale">
|
||
<h2><a class="toc-backref" href="#rationale" role="doc-backlink">Rationale</a></h2>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">Callable</span></code> type is widely used. For example, <a class="reference external" href="https://github.com/pradeep90/annotation_collector#overall-stats-in-typeshed">as of October 2021
|
||
it was</a>
|
||
the fifth most common complex type in typeshed, after <code class="docutils literal notranslate"><span class="pre">Optional</span></code>,
|
||
<code class="docutils literal notranslate"><span class="pre">Tuple</span></code>, <code class="docutils literal notranslate"><span class="pre">Union</span></code>, and <code class="docutils literal notranslate"><span class="pre">List</span></code>.</p>
|
||
<p>The others have had their syntax improved and the need for imports
|
||
eliminated by either <a class="pep reference internal" href="../pep-0604/" title="PEP 604 – Allow writing union types as X | Y">PEP 604</a> or <a class="pep reference internal" href="../pep-0585/" title="PEP 585 – Type Hinting Generics In Standard Collections">PEP 585</a>:</p>
|
||
<ul class="simple">
|
||
<li><code class="docutils literal notranslate"><span class="pre">typing.Optional[int]</span></code> is written <code class="docutils literal notranslate"><span class="pre">int</span> <span class="pre">|</span> <span class="pre">None</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">typing.Union[int,</span> <span class="pre">str]</span></code> is written <code class="docutils literal notranslate"><span class="pre">int</span> <span class="pre">|</span> <span class="pre">str</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">typing.List[int]</span></code> is written <code class="docutils literal notranslate"><span class="pre">list[int]</span></code></li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">typing.Tuple[int,</span> <span class="pre">str]</span></code> is written <code class="docutils literal notranslate"><span class="pre">tuple[int,</span> <span class="pre">str]</span></code></li>
|
||
</ul>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code> type is used almost as often as these other
|
||
types, is more complicated to read and write, and still requires an
|
||
import and bracket-based syntax.</p>
|
||
<p>In this proposal, we chose to support all the existing semantics of
|
||
<code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>, without adding support for new features. We made
|
||
this decision after examining how frequently each feature might be
|
||
used in existing typed and untyped open-source code. We determined
|
||
that the vast majority of use cases are covered.</p>
|
||
<p>We considered adding support for named, optional, and variadic
|
||
arguments. However, we decided against including these features, as
|
||
our analysis showed they are infrequently used. When they are really
|
||
needed, it is possible to type these using <a class="reference external" href="https://mypy.readthedocs.io/en/stable/protocols.html#callback-protocols">callback protocols</a>.</p>
|
||
<section id="an-arrow-syntax-for-callable-types">
|
||
<h3><a class="toc-backref" href="#an-arrow-syntax-for-callable-types" role="doc-backlink">An Arrow Syntax for Callable Types</a></h3>
|
||
<p>We are proposing a succinct, easy-to-use syntax for
|
||
<code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code> that looks similar to function headers in Python.
|
||
Our proposal closely follows syntax used by several popular languages
|
||
such as <a class="reference external" href="https://basarat.gitbook.io/typescript/type-system/callable#arrow-syntax">Typescript</a>,
|
||
<a class="reference external" href="https://kotlinlang.org/docs/lambdas.html">Kotlin</a>, and <a class="reference external" href="https://docs.scala-lang.org/tour/higher-order-functions.html">Scala</a>.</p>
|
||
<p>Our goals are that:</p>
|
||
<ul class="simple">
|
||
<li>Callable types using this syntax will be easier to learn and use,
|
||
particularly for developers with experience in other languages.</li>
|
||
<li>Library authors will be more likely to use expressive types for
|
||
callables that enable type checkers to better understand code and
|
||
find bugs, as in the <code class="docutils literal notranslate"><span class="pre">decorator</span></code> example above.</li>
|
||
</ul>
|
||
<p>Consider this simplified real-world example from a web server, written
|
||
using the existing <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Awaitable</span><span class="p">,</span> <span class="n">Callable</span>
|
||
<span class="kn">from</span> <span class="nn">app_logic</span> <span class="kn">import</span> <span class="n">Response</span><span class="p">,</span> <span class="n">UserSetting</span>
|
||
|
||
|
||
<span class="k">def</span> <span class="nf">customize_response</span><span class="p">(</span>
|
||
<span class="n">response</span><span class="p">:</span> <span class="n">Response</span><span class="p">,</span>
|
||
<span class="n">customizer</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="n">Response</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="n">UserSetting</span><span class="p">]],</span> <span class="n">Awaitable</span><span class="p">[</span><span class="n">Response</span><span class="p">]]</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">Response</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>With our proposal, this code can be abbreviated to:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">app_logic</span> <span class="kn">import</span> <span class="n">Response</span><span class="p">,</span> <span class="n">UserSetting</span>
|
||
|
||
<span class="k">def</span> <span class="nf">customize_response</span><span class="p">(</span>
|
||
<span class="n">response</span><span class="p">:</span> <span class="n">Response</span><span class="p">,</span>
|
||
<span class="n">customizer</span><span class="p">:</span> <span class="k">async</span> <span class="p">(</span><span class="n">Response</span><span class="p">,</span> <span class="nb">list</span><span class="p">[</span><span class="n">UserSetting</span><span class="p">])</span> <span class="o">-></span> <span class="n">Response</span><span class="p">,</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">Response</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This is shorter and requires fewer imports. It also has far less
|
||
nesting of square brackets - only one level, as opposed to three in
|
||
the original code.</p>
|
||
</section>
|
||
<section id="compact-syntax-for-paramspec">
|
||
<h3><a class="toc-backref" href="#compact-syntax-for-paramspec" role="doc-backlink">Compact Syntax for <code class="docutils literal notranslate"><span class="pre">ParamSpec</span></code></a></h3>
|
||
<p>A particularly common case where library authors leave off type information
|
||
for callables is when defining decorators. Consider the following:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Callable</span>
|
||
|
||
<span class="k">def</span> <span class="nf">with_retries</span><span class="p">(</span>
|
||
<span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="n">Any</span><span class="p">]</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">Callable</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="n">Any</span><span class="p">]:</span>
|
||
<span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">retry_once</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="n">retry_once</span><span class="p">:</span>
|
||
<span class="k">try</span><span class="p">:</span> <span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
<span class="k">except</span> <span class="ne">Exception</span><span class="p">:</span> <span class="k">pass</span>
|
||
<span class="k">return</span> <span class="n">f</span><span class="p">(</span><span class="o">*</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="n">wrapper</span>
|
||
|
||
<span class="nd">@with_retries</span>
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">x</span>
|
||
|
||
|
||
<span class="n">f</span><span class="p">(</span><span class="n">y</span><span class="o">=</span><span class="mi">10</span><span class="p">)</span> <span class="c1"># oops - no type error!</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>In the code above, it is clear that the decorator should produce a
|
||
function whose signature is like that of the argument <code class="docutils literal notranslate"><span class="pre">f</span></code> other
|
||
than an additional <code class="docutils literal notranslate"><span class="pre">retry_once</span></code> argument. But the use of <code class="docutils literal notranslate"><span class="pre">...</span></code>
|
||
prevents a type checker from seeing this and alerting a user that
|
||
<code class="docutils literal notranslate"><span class="pre">f(y=10)</span></code> is invalid.</p>
|
||
<p>With <a class="pep reference internal" href="../pep-0612/" title="PEP 612 – Parameter Specification Variables">PEP 612</a> it is possible to type decorators like this correctly
|
||
as follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">Concatenate</span><span class="p">,</span> <span class="n">ParamSpec</span><span class="p">,</span> <span class="n">TypeVar</span>
|
||
|
||
<span class="n">R</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"R"</span><span class="p">)</span>
|
||
<span class="n">P</span> <span class="o">=</span> <span class="n">ParamSpec</span><span class="p">(</span><span class="s2">"P"</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">with_retries</span><span class="p">(</span>
|
||
<span class="n">f</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="n">R</span><span class="p">]</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">Callable</span><span class="p">[</span><span class="n">Concatenate</span><span class="p">[</span><span class="nb">bool</span><span class="p">,</span> <span class="n">P</span><span class="p">]</span> <span class="n">R</span><span class="p">]:</span>
|
||
<span class="k">def</span> <span class="nf">wrapper</span><span class="p">(</span><span class="n">retry_once</span><span class="p">:</span> <span class="nb">bool</span><span class="p">,</span> <span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">args</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="n">P</span><span class="o">.</span><span class="n">kwargs</span><span class="p">)</span> <span class="o">-></span> <span class="n">R</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
<span class="k">return</span> <span class="n">wrapper</span>
|
||
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>With our proposed syntax, the properly-typed decorator example becomes
|
||
concise and the type representations are visually descriptive:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Any</span><span class="p">,</span> <span class="n">ParamSpec</span><span class="p">,</span> <span class="n">TypeVar</span>
|
||
|
||
<span class="n">R</span> <span class="o">=</span> <span class="n">TypeVar</span><span class="p">(</span><span class="s2">"R"</span><span class="p">)</span>
|
||
<span class="n">P</span> <span class="o">=</span> <span class="n">ParamSpec</span><span class="p">(</span><span class="s2">"P"</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">with_retries</span><span class="p">(</span>
|
||
<span class="n">f</span><span class="p">:</span> <span class="p">(</span><span class="o">**</span><span class="n">P</span><span class="p">)</span> <span class="o">-></span> <span class="n">R</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="nb">bool</span><span class="p">,</span> <span class="o">**</span><span class="n">P</span><span class="p">)</span> <span class="o">-></span> <span class="n">R</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="comparing-to-other-languages">
|
||
<h3><a class="toc-backref" href="#comparing-to-other-languages" role="doc-backlink">Comparing to Other Languages</a></h3>
|
||
<p>Many popular programming languages use an arrow syntax similar
|
||
to the one we are proposing here.</p>
|
||
<section id="id1">
|
||
<h4><a class="toc-backref" href="#id1" role="doc-backlink">TypeScript</a></h4>
|
||
<p>In <a class="reference external" href="https://basarat.gitbook.io/typescript/type-system/callable#arrow-syntax">TypeScript</a>,
|
||
function types are expressed in a syntax almost the same as the one we
|
||
are proposing, but the arrow token is <code class="docutils literal notranslate"><span class="pre">=></span></code> and arguments have names:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">=></span> <span class="nb">bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The names of the arguments are not actually relevant to the type. So,
|
||
for example, this is the same callable type:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="n">b</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">=></span> <span class="nb">bool</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="id3">
|
||
<h4><a class="toc-backref" href="#id3" role="doc-backlink">Kotlin</a></h4>
|
||
<p>Function types in <a class="reference external" href="https://kotlinlang.org/docs/lambdas.html">Kotlin</a> permit
|
||
an identical syntax to the one we are proposing, for example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">Int</span><span class="p">,</span> <span class="n">String</span><span class="p">)</span> <span class="o">-></span> <span class="n">Bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>It also optionally allows adding names to the arguments, for example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="o">-></span> <span class="n">Bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>As in TypeScript, the argument names (if provided) are just there for
|
||
documentation and are not part of the type itself.</p>
|
||
</section>
|
||
<section id="id5">
|
||
<h4><a class="toc-backref" href="#id5" role="doc-backlink">Scala</a></h4>
|
||
<p><a class="reference external" href="https://docs.scala-lang.org/tour/higher-order-functions.html">Scala</a>
|
||
uses the <code class="docutils literal notranslate"><span class="pre">=></span></code> arrow for function types. Other than that, their syntax is
|
||
the same as the one we are proposing, for example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">Int</span><span class="p">,</span> <span class="n">String</span><span class="p">)</span> <span class="o">=></span> <span class="n">Bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Scala, like Python, has the ability to provide function arguments by name.
|
||
Function types can optionally include names, for example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="n">Int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="n">String</span><span class="p">)</span> <span class="o">=></span> <span class="n">Bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Unlike in TypeScript and Kotlin, these names are part of the type if
|
||
provided - any function implementing the type must use the same names.
|
||
This is similar to the extended syntax proposal we describe in our
|
||
<a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a> section.</p>
|
||
</section>
|
||
<section id="function-definitions-vs-callable-type-annotations">
|
||
<h4><a class="toc-backref" href="#function-definitions-vs-callable-type-annotations" role="doc-backlink">Function Definitions vs Callable Type Annotations</a></h4>
|
||
<p>In all of the languages listed above, type annotations for function
|
||
definitions use a <code class="docutils literal notranslate"><span class="pre">:</span></code> rather than a <code class="docutils literal notranslate"><span class="pre">-></span></code>. For example, in TypeScript
|
||
a simple add function looks like this:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">function</span> <span class="n">higher_order</span><span class="p">(</span><span class="n">fn</span><span class="p">:</span> <span class="p">(</span><span class="n">a</span><span class="p">:</span> <span class="n">string</span><span class="p">)</span> <span class="o">=></span> <span class="n">string</span><span class="p">):</span> <span class="n">string</span> <span class="p">{</span>
|
||
<span class="k">return</span> <span class="n">fn</span><span class="p">(</span><span class="s2">"Hello, World"</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Scala and Kotlin use essentially the same <code class="docutils literal notranslate"><span class="pre">:</span></code> syntax for return
|
||
annotations. The <code class="docutils literal notranslate"><span class="pre">:</span></code> makes sense in these languages because they
|
||
all use <code class="docutils literal notranslate"><span class="pre">:</span></code> for type annotations of
|
||
parameters and variables, and the use for function return types is
|
||
similar.</p>
|
||
<p>In Python we use <code class="docutils literal notranslate"><span class="pre">:</span></code> to denote the start of a function body and
|
||
<code class="docutils literal notranslate"><span class="pre">-></span></code> for return annotations. As a result, even though our proposal
|
||
is superficially the same as these other languages the context is
|
||
different. There is potential for more confusion in Python when
|
||
reading function definitions that include callable types.</p>
|
||
<p>This is a key concern for which we are seeking feedback with our draft
|
||
PEP; one idea we have floated is to use <code class="docutils literal notranslate"><span class="pre">=></span></code> instead to make it easier
|
||
to differentiate.</p>
|
||
</section>
|
||
<section id="the-ml-language-family">
|
||
<h4><a class="toc-backref" href="#the-ml-language-family" role="doc-backlink">The ML Language Family</a></h4>
|
||
<p>Languages in the ML family, including <a class="reference external" href="https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/fsharp-types#syntax-for-types">F#</a>,
|
||
<a class="reference external" href="https://www2.ocaml.org/learn/tutorials/basics.html#Defining-a-function">OCaml</a>,
|
||
and <a class="reference external" href="https://wiki.haskell.org/Type_signature">Haskell</a>, all use
|
||
<code class="docutils literal notranslate"><span class="pre">-></span></code> to represent function types. All of them use a parentheses-free
|
||
syntax with multiple arrows, for example in Haskell:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">Integer</span> <span class="o">-></span> <span class="n">String</span> <span class="o">-></span> <span class="n">Bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The use of multiple arrows, which differs from our proposal, makes
|
||
sense for languages in this family because they use automatic
|
||
<a class="reference external" href="https://en.wikipedia.org/wiki/Currying">currying</a> of function arguments,
|
||
which means that a multi-argument function behaves like a single-argument
|
||
function returning a function.</p>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="specification">
|
||
<h2><a class="toc-backref" href="#specification" role="doc-backlink">Specification</a></h2>
|
||
<section id="typing-behavior">
|
||
<h3><a class="toc-backref" href="#typing-behavior" role="doc-backlink">Typing Behavior</a></h3>
|
||
<p>Type checkers should treat the new syntax with exactly the same
|
||
semantics as <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>.</p>
|
||
<p>As such, a type checker should treat the following pairs exactly the
|
||
same:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">Awaitable</span><span class="p">,</span> <span class="n">Callable</span><span class="p">,</span> <span class="n">Concatenate</span><span class="p">,</span> <span class="n">ParamSpec</span><span class="p">,</span> <span class="n">TypeVarTuple</span>
|
||
|
||
<span class="n">P</span> <span class="o">=</span> <span class="n">ParamSpec</span><span class="p">(</span><span class="s2">"P"</span><span class="p">)</span>
|
||
<span class="n">Ts</span> <span class="o">=</span> <span class="n">TypeVarTuple</span><span class="p">(</span><span class="s1">'Ts'</span><span class="p">)</span>
|
||
|
||
<span class="n">f0</span><span class="p">:</span> <span class="p">()</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="n">f0</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[],</span> <span class="nb">bool</span><span class="p">]</span>
|
||
|
||
<span class="n">f1</span><span class="p">:</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="n">f1</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">],</span> <span class="nb">bool</span><span class="p">]</span>
|
||
|
||
<span class="n">f2</span><span class="p">:</span> <span class="p">(</span><span class="o">...</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="n">f2</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="o">...</span><span class="p">,</span> <span class="nb">bool</span><span class="p">]</span>
|
||
|
||
<span class="n">f3</span><span class="p">:</span> <span class="k">async</span> <span class="p">(</span><span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span>
|
||
<span class="n">f3</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="nb">str</span><span class="p">],</span> <span class="n">Awaitable</span><span class="p">[</span><span class="nb">str</span><span class="p">]]</span>
|
||
|
||
<span class="n">f4</span><span class="p">:</span> <span class="p">(</span><span class="o">**</span><span class="n">P</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="n">f4</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">P</span><span class="p">,</span> <span class="nb">bool</span><span class="p">]</span>
|
||
|
||
<span class="n">f5</span><span class="p">:</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="o">**</span><span class="n">P</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="n">f5</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[</span><span class="n">Concatenate</span><span class="p">[</span><span class="nb">int</span><span class="p">,</span> <span class="n">P</span><span class="p">],</span> <span class="nb">bool</span><span class="p">]</span>
|
||
|
||
<span class="n">f6</span><span class="p">:</span> <span class="p">(</span><span class="o">*</span><span class="n">Ts</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="n">f6</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="o">*</span><span class="n">Ts</span><span class="p">],</span> <span class="nb">bool</span><span class="p">]</span>
|
||
|
||
<span class="n">f7</span><span class="p">:</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="o">*</span><span class="n">Ts</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="n">f7</span><span class="p">:</span> <span class="n">Callable</span><span class="p">[[</span><span class="nb">int</span><span class="p">,</span> <span class="o">*</span><span class="n">Ts</span><span class="p">,</span> <span class="nb">str</span><span class="p">],</span> <span class="nb">bool</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="grammar-and-ast">
|
||
<h3><a class="toc-backref" href="#grammar-and-ast" role="doc-backlink">Grammar and AST</a></h3>
|
||
<p>The proposed new syntax can be described by these AST changes to <a class="reference external" href="https://github.com/python/cpython/blob/main/Parser/Python.asdl">Parser/Python.asdl</a>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">expr</span> <span class="o">=</span> <span class="o"><</span><span class="n">prexisting_expr_kinds</span><span class="o">></span>
|
||
<span class="o">|</span> <span class="n">AsyncCallableType</span><span class="p">(</span><span class="n">callable_type_arguments</span> <span class="n">args</span><span class="p">,</span> <span class="n">expr</span> <span class="n">returns</span><span class="p">)</span>
|
||
<span class="o">|</span> <span class="n">CallableType</span><span class="p">(</span><span class="n">callable_type_arguments</span> <span class="n">args</span><span class="p">,</span> <span class="n">expr</span> <span class="n">returns</span><span class="p">)</span>
|
||
|
||
<span class="n">callable_type_arguments</span> <span class="o">=</span> <span class="n">AnyArguments</span>
|
||
<span class="o">|</span> <span class="n">ArgumentsList</span><span class="p">(</span><span class="n">expr</span><span class="o">*</span> <span class="n">posonlyargs</span><span class="p">)</span>
|
||
<span class="o">|</span> <span class="n">Concatenation</span><span class="p">(</span><span class="n">expr</span><span class="o">*</span> <span class="n">posonlyargs</span><span class="p">,</span> <span class="n">expr</span> <span class="n">param_spec</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Here are our proposed changes to the <cite>Python Grammar
|
||
<https://docs.python.org/3/reference/grammar.htm></cite>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>expression:
|
||
| disjunction disjunction 'else' expression
|
||
| callable_type_expression
|
||
| disjunction
|
||
| lambdef
|
||
|
||
callable_type_expression:
|
||
| callable_type_arguments '->' expression
|
||
| ASYNC callable_type_arguments '->' expression
|
||
|
||
callable_type_arguments:
|
||
| '(' '...' [','] ')'
|
||
| '(' callable_type_positional_argument* ')'
|
||
| '(' callable_type_positional_argument* callable_type_param_spec ')'
|
||
|
||
callable_type_positional_argument:
|
||
| !'...' expression ','
|
||
| !'...' expression &')'
|
||
|
||
callable_type_param_spec:
|
||
| '**' expression ','
|
||
| '**' expression &')'
|
||
</pre></div>
|
||
</div>
|
||
<p>If <a class="pep reference internal" href="../pep-0646/" title="PEP 646 – Variadic Generics">PEP 646</a> is accepted, we intend to include support for unpacked
|
||
types in two ways. To support the “star-for-unpack” syntax proposed in
|
||
<a class="pep reference internal" href="../pep-0646/" title="PEP 646 – Variadic Generics">PEP 646</a>, we will modify the grammar for
|
||
<code class="docutils literal notranslate"><span class="pre">callable_type_positional_argument</span></code> as follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span>callable_type_positional_argument:
|
||
| !'...' expression ','
|
||
| !'...' expression &')'
|
||
| '*' expression ','
|
||
| '*' expression &')'
|
||
</pre></div>
|
||
</div>
|
||
<p>With this change, a type of the form <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">*Ts)</span> <span class="pre">-></span> <span class="pre">bool</span></code> should
|
||
evaluate the AST form:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">CallableType</span><span class="p">(</span>
|
||
<span class="n">ArgumentsList</span><span class="p">(</span><span class="n">Name</span><span class="p">(</span><span class="s2">"int"</span><span class="p">),</span> <span class="n">Starred</span><span class="p">(</span><span class="n">Name</span><span class="p">(</span><span class="s2">"Ts"</span><span class="p">)),</span>
|
||
<span class="n">Name</span><span class="p">(</span><span class="s2">"bool"</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>and be treated by type checkers as equivalent to or <code class="docutils literal notranslate"><span class="pre">Callable[[int,</span>
|
||
<span class="pre">*Ts],</span> <span class="pre">bool]</span></code> or <code class="docutils literal notranslate"><span class="pre">Callable[[int,</span> <span class="pre">Unpack[Ts]],</span> <span class="pre">bool]</span></code>.</p>
|
||
</section>
|
||
<section id="implications-of-the-grammar">
|
||
<h3><a class="toc-backref" href="#implications-of-the-grammar" role="doc-backlink">Implications of the Grammar</a></h3>
|
||
<section id="precedence-of">
|
||
<h4><a class="toc-backref" href="#precedence-of" role="doc-backlink">Precedence of -></a></h4>
|
||
<p><code class="docutils literal notranslate"><span class="pre">-></span></code> binds less tightly than other operators, both inside types and
|
||
in function signatures, so the following two callable types are
|
||
equivalent:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span> <span class="o">|</span> <span class="nb">bool</span>
|
||
<span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="nb">str</span> <span class="o">|</span> <span class="nb">bool</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p><code class="docutils literal notranslate"><span class="pre">-></span></code> associates to the right, both inside types and in function
|
||
signatures. So the following pairs are equivalent:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">((</span><span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span> <span class="k">pass</span>
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="p">((</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">):</span> <span class="k">pass</span>
|
||
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span> <span class="k">pass</span>
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="p">((</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">((</span><span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">)):</span> <span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Because operators bind more tightly than <code class="docutils literal notranslate"><span class="pre">-></span></code>, parentheses are
|
||
required whenever an arrow type is intended to be inside an argument
|
||
to an operator like <code class="docutils literal notranslate"><span class="pre">|</span></code>:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">()</span> <span class="o">-></span> <span class="nb">int</span> <span class="o">|</span> <span class="p">()</span> <span class="o">-></span> <span class="nb">bool</span> <span class="c1"># syntax error!</span>
|
||
<span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">(()</span> <span class="o">-></span> <span class="nb">int</span><span class="p">)</span> <span class="o">|</span> <span class="p">(()</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">)</span> <span class="c1"># okay</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We discussed each of these behaviors and believe they are desirable:</p>
|
||
<ul class="simple">
|
||
<li>Union types (represented by <code class="docutils literal notranslate"><span class="pre">A</span> <span class="pre">|</span> <span class="pre">B</span></code> according to <a class="pep reference internal" href="../pep-0604/" title="PEP 604 – Allow writing union types as X | Y">PEP 604</a>) are
|
||
valid in function signature returns, so we need to allow operators
|
||
in the return position for consistency.</li>
|
||
<li>Given that operators bind more tightly than <code class="docutils literal notranslate"><span class="pre">-></span></code> it is correct
|
||
that a type like <code class="docutils literal notranslate"><span class="pre">bool</span> <span class="pre">|</span> <span class="pre">()</span> <span class="pre">-></span> <span class="pre">bool</span></code> must be a syntax error. We
|
||
should be sure the error message is clear because this may be a
|
||
common mistake.</li>
|
||
<li>Associating <code class="docutils literal notranslate"><span class="pre">-></span></code> to the right, rather than requiring explicit
|
||
parentheses, is consistent with other languages like TypeScript and
|
||
respects the principle that valid expressions should normally be
|
||
substitutable when possible.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="async-keyword">
|
||
<h4><a class="toc-backref" href="#async-keyword" role="doc-backlink"><code class="docutils literal notranslate"><span class="pre">async</span></code> Keyword</a></h4>
|
||
<p>All of the binding rules still work for async callable types:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="k">async</span> <span class="p">(</span><span class="nb">float</span><span class="p">)</span> <span class="o">-></span> <span class="nb">str</span> <span class="o">|</span> <span class="nb">bool</span>
|
||
<span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="nb">float</span><span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="nb">str</span> <span class="o">|</span> <span class="nb">bool</span><span class="p">))</span>
|
||
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="k">async</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span> <span class="k">pass</span>
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">):</span> <span class="k">pass</span>
|
||
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="k">async</span> <span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="k">async</span> <span class="p">(</span><span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span> <span class="k">pass</span>
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">()</span> <span class="o">-></span> <span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">)):</span> <span class="k">pass</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="trailing-commas">
|
||
<h4><a class="toc-backref" href="#trailing-commas" role="doc-backlink">Trailing Commas</a></h4>
|
||
<ul>
|
||
<li>Following the precedent of function signatures, putting a comma in
|
||
an empty arguments list is illegal: <code class="docutils literal notranslate"><span class="pre">(,)</span> <span class="pre">-></span> <span class="pre">bool</span></code> is a syntax
|
||
error.</li>
|
||
<li>Again following precedent, trailing commas are otherwise always
|
||
permitted:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">((</span><span class="nb">int</span><span class="p">,)</span> <span class="o">-></span> <span class="nb">bool</span> <span class="o">==</span> <span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="p">((</span><span class="nb">int</span><span class="p">,</span> <span class="o">**</span><span class="n">P</span><span class="p">,)</span> <span class="o">-></span> <span class="nb">bool</span> <span class="o">==</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="o">**</span><span class="n">P</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
<span class="p">((</span><span class="o">...</span><span class="p">,)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">)</span> <span class="o">==</span> <span class="p">((</span><span class="o">...</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
<p>Allowing trailing commas also gives autoformatters more flexibility
|
||
when splitting callable types across lines, which is always legal
|
||
following standard python whitespace rules.</p>
|
||
</section>
|
||
<section id="disallowing-as-an-argument-type">
|
||
<h4><a class="toc-backref" href="#disallowing-as-an-argument-type" role="doc-backlink">Disallowing <code class="docutils literal notranslate"><span class="pre">...</span></code> as an Argument Type</a></h4>
|
||
<p>Under normal circumstances, any valid expression is permitted where we
|
||
want a type annotation and <code class="docutils literal notranslate"><span class="pre">...</span></code> is a valid expression. This is
|
||
never semantically valid and all type checkers would reject it, but
|
||
the grammar would allow it if we did not explicitly prevent this.</p>
|
||
<p>Since <code class="docutils literal notranslate"><span class="pre">...</span></code> is meaningless as a type and there are usability
|
||
concerns, our grammar rules it out and the following is a syntax
|
||
error:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="o">...</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We decided that there were compelling reasons to do this:</p>
|
||
<ul class="simple">
|
||
<li>The semantics of <code class="docutils literal notranslate"><span class="pre">(...)</span> <span class="pre">-></span> <span class="pre">bool</span></code> are different from <code class="docutils literal notranslate"><span class="pre">(T)</span> <span class="pre">-></span> <span class="pre">bool</span></code>
|
||
for any valid type T: <code class="docutils literal notranslate"><span class="pre">(...)</span></code> is a special form indicating
|
||
<code class="docutils literal notranslate"><span class="pre">AnyArguments</span></code> whereas <code class="docutils literal notranslate"><span class="pre">T</span></code> is a type parameter in the arguments
|
||
list.</li>
|
||
<li><code class="docutils literal notranslate"><span class="pre">...</span></code> is used as a placeholder default value to indicate an
|
||
optional argument in stubs and callback protocols. Allowing it in
|
||
the position of a type could easily lead to confusion and possibly
|
||
bugs due to typos.</li>
|
||
<li>In the <code class="docutils literal notranslate"><span class="pre">tuple</span></code> generic type, we special-case <code class="docutils literal notranslate"><span class="pre">...</span></code> to mean
|
||
“more of the same”, e.g. a <code class="docutils literal notranslate"><span class="pre">tuple[int,</span> <span class="pre">...]</span></code> means a tuple with
|
||
one or more integers. We do not use <code class="docutils literal notranslate"><span class="pre">...</span></code> in a a similar way
|
||
in callable types, so to prevent misunderstandings it makes sense
|
||
to prevent this.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="incompatibility-with-other-possible-uses-of-and">
|
||
<h4><a class="toc-backref" href="#incompatibility-with-other-possible-uses-of-and" role="doc-backlink">Incompatibility with other possible uses of <code class="docutils literal notranslate"><span class="pre">*</span></code> and <code class="docutils literal notranslate"><span class="pre">**</span></code></a></h4>
|
||
<p>The use of <code class="docutils literal notranslate"><span class="pre">**P</span></code> for supporting <a class="pep reference internal" href="../pep-0612/" title="PEP 612 – Parameter Specification Variables">PEP 612</a> <code class="docutils literal notranslate"><span class="pre">ParamSpec</span></code> rules out any
|
||
future proposal using a bare <code class="docutils literal notranslate"><span class="pre">**<some_type></span></code> to type
|
||
<code class="docutils literal notranslate"><span class="pre">kwargs</span></code>. This seems acceptable because:</p>
|
||
<ul class="simple">
|
||
<li>If we ever do want such a syntax, it would be clearer to require an
|
||
argument name anyway. This would also make the type look more
|
||
similar to a function signature. In other words, if we ever support
|
||
typing <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> in callable types, we would prefer <code class="docutils literal notranslate"><span class="pre">(int,</span>
|
||
<span class="pre">**kwargs:</span> <span class="pre">str)</span></code> rather than <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">**str)</span></code>.</li>
|
||
<li><a class="pep reference internal" href="../pep-0646/" title="PEP 646 – Variadic Generics">PEP 646</a> unpacking syntax would rule out using <code class="docutils literal notranslate"><span class="pre">*<some_type></span></code> for
|
||
<code class="docutils literal notranslate"><span class="pre">args</span></code>. The <code class="docutils literal notranslate"><span class="pre">kwargs</span></code> case is similar enough that this rules out
|
||
a bare <code class="docutils literal notranslate"><span class="pre">**<some_type></span></code> anyway.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="compatibility-with-arrow-based-lambda-syntax">
|
||
<h4><a class="toc-backref" href="#compatibility-with-arrow-based-lambda-syntax" role="doc-backlink">Compatibility with Arrow-Based Lambda Syntax</a></h4>
|
||
<p>To the best of our knowledge there is no active discussion of
|
||
arrow-style lambda syntax that we are aware of, but it is nonetheless
|
||
worth considering what possibilities would be ruled out by adopting
|
||
this proposal.</p>
|
||
<p>It would be incompatible with this proposal to adopt the same a
|
||
parenthesized <code class="docutils literal notranslate"><span class="pre">-></span></code>-based arrow syntax for lambdas, e.g. <code class="docutils literal notranslate"><span class="pre">(x,</span> <span class="pre">y)</span> <span class="pre">-></span>
|
||
<span class="pre">x</span> <span class="pre">+</span> <span class="pre">y</span></code> for <code class="docutils literal notranslate"><span class="pre">lambda</span> <span class="pre">x,</span> <span class="pre">y:</span> <span class="pre">x</span> <span class="pre">+</span> <span class="pre">y</span></code>.</p>
|
||
<p>Our view is that if we want arrow syntax for lambdas in the future, it
|
||
would be a better choice to use <code class="docutils literal notranslate"><span class="pre">=></span></code>, e.g. <code class="docutils literal notranslate"><span class="pre">(x,</span> <span class="pre">y)</span> <span class="pre">=></span> <span class="pre">x</span> <span class="pre">+</span> <span class="pre">y</span></code>.
|
||
Many languages use the same arrow token for both lambdas and callable
|
||
types, but Python is unique in that types are expressions and have to
|
||
evaluate to runtime values. Our view is that this merits using
|
||
separate tokens, and given the existing use of <code class="docutils literal notranslate"><span class="pre">-></span></code> for return types
|
||
in function signatures it would be more coherent to use <code class="docutils literal notranslate"><span class="pre">-></span></code> for
|
||
callable types and <code class="docutils literal notranslate"><span class="pre">=></span></code> for lambdas.</p>
|
||
</section>
|
||
</section>
|
||
<section id="runtime-behavior">
|
||
<h3><a class="toc-backref" href="#runtime-behavior" role="doc-backlink">Runtime Behavior</a></h3>
|
||
<p>The new AST nodes need to evaluate to runtime types, and we have two goals for the
|
||
behavior of these runtime types:</p>
|
||
<ul class="simple">
|
||
<li>They should expose a structured API that is descriptive and powerful
|
||
enough to be compatible with extending the type to include new features
|
||
like named and variadic arguments.</li>
|
||
<li>They should also expose an API that is backward-compatible with
|
||
<code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>.</li>
|
||
</ul>
|
||
<section id="evaluation-and-structured-api">
|
||
<h4><a class="toc-backref" href="#evaluation-and-structured-api" role="doc-backlink">Evaluation and Structured API</a></h4>
|
||
<p>We intend to create new builtin types to which the new AST nodes will
|
||
evaluate, exposing them in the <code class="docutils literal notranslate"><span class="pre">types</span></code> module.</p>
|
||
<p>Our plan is to expose a structured API as if they were defined as follows:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">CallableType</span><span class="p">:</span>
|
||
<span class="n">is_async</span><span class="p">:</span> <span class="nb">bool</span>
|
||
<span class="n">arguments</span><span class="p">:</span> <span class="bp">Ellipsis</span> <span class="o">|</span> <span class="nb">tuple</span><span class="p">[</span><span class="n">CallableTypeArgument</span><span class="p">]</span>
|
||
<span class="n">return_type</span><span class="p">:</span> <span class="nb">object</span>
|
||
|
||
<span class="k">class</span> <span class="nc">CallableTypeArgument</span><span class="p">:</span>
|
||
<span class="n">kind</span><span class="p">:</span> <span class="n">CallableTypeArgumentKind</span>
|
||
<span class="n">annotation</span><span class="p">:</span> <span class="nb">object</span>
|
||
|
||
<span class="nd">@enum</span><span class="o">.</span><span class="n">global_enum</span>
|
||
<span class="k">class</span> <span class="nc">CallableTypeArgumentKind</span><span class="p">(</span><span class="n">enum</span><span class="o">.</span><span class="n">IntEnum</span><span class="p">):</span>
|
||
<span class="n">POSITIONAL_ONLY</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="o">...</span>
|
||
<span class="n">PARAM_SPEC</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The evaluation rules are expressed in terms of the following
|
||
pseudocode:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">evaluate_callable_type</span><span class="p">(</span>
|
||
<span class="n">callable_type</span><span class="p">:</span> <span class="n">ast</span><span class="o">.</span><span class="n">CallableType</span> <span class="o">|</span> <span class="n">ast</span><span class="o">.</span><span class="n">AsyncCallableType</span><span class="p">:</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="n">CallableType</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="n">CallableType</span><span class="p">(</span>
|
||
<span class="n">is_async</span><span class="o">=</span><span class="nb">isinstance</span><span class="p">(</span><span class="n">callable_type</span><span class="p">,</span> <span class="n">ast</span><span class="o">.</span><span class="n">AsyncCallableType</span><span class="p">),</span>
|
||
<span class="n">arguments</span><span class="o">=</span><span class="n">_evaluate_arguments</span><span class="p">(</span><span class="n">callable_type</span><span class="o">.</span><span class="n">arguments</span><span class="p">),</span>
|
||
<span class="n">return_type</span><span class="o">=</span><span class="n">evaluate_expression</span><span class="p">(</span><span class="n">callable_type</span><span class="o">.</span><span class="n">returns</span><span class="p">),</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">_evaluate_arguments</span><span class="p">(</span><span class="n">arguments</span><span class="p">):</span>
|
||
<span class="k">match</span> <span class="n">arguments</span><span class="p">:</span>
|
||
<span class="k">case</span> <span class="n">ast</span><span class="o">.</span><span class="n">AnyArguments</span><span class="p">():</span>
|
||
<span class="k">return</span> <span class="bp">Ellipsis</span>
|
||
<span class="k">case</span> <span class="n">ast</span><span class="o">.</span><span class="n">ArgumentsList</span><span class="p">(</span><span class="n">posonlyargs</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span>
|
||
<span class="n">_evaluate_arg</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span>
|
||
<span class="p">)</span>
|
||
<span class="k">case</span> <span class="n">ast</span><span class="o">.</span><span class="n">ArgumentsListConcatenation</span><span class="p">(</span><span class="n">posonlyargs</span><span class="p">,</span> <span class="n">param_spec</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span>
|
||
<span class="o">*</span><span class="p">(</span><span class="n">evaluate_arg</span><span class="p">(</span><span class="n">arg</span><span class="p">)</span> <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">args</span><span class="p">),</span>
|
||
<span class="n">_evaluate_arg</span><span class="p">(</span><span class="n">arg</span><span class="o">=</span><span class="n">param_spec</span><span class="p">,</span> <span class="n">kind</span><span class="o">=</span><span class="n">PARAM_SPEC</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">arguments</span><span class="p">,</span> <span class="n">Any</span>
|
||
<span class="k">return</span> <span class="bp">Ellipsis</span>
|
||
|
||
<span class="k">def</span> <span class="nf">_evaluate_arg</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">kind</span><span class="o">=</span><span class="n">POSITIONAL_ONLY</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="n">CallableTypeArgument</span><span class="p">(</span>
|
||
<span class="n">kind</span><span class="o">=</span><span class="n">POSITIONAL_ONLY</span><span class="p">,</span>
|
||
<span class="n">annotation</span><span class="o">=</span><span class="n">evaluate_expression</span><span class="p">(</span><span class="n">value</span><span class="p">)</span>
|
||
<span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="backward-compatible-api">
|
||
<h4><a class="toc-backref" href="#backward-compatible-api" role="doc-backlink">Backward-Compatible API</a></h4>
|
||
<p>To get backward compatibility with the existing <code class="docutils literal notranslate"><span class="pre">types.Callable</span></code> API,
|
||
which relies on fields <code class="docutils literal notranslate"><span class="pre">__args__</span></code> and <code class="docutils literal notranslate"><span class="pre">__parameters__</span></code>, we can define
|
||
them as if they were written in terms of the following:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">itertools</span>
|
||
<span class="kn">import</span> <span class="nn">typing</span>
|
||
|
||
<span class="k">def</span> <span class="nf">get_args</span><span class="p">(</span><span class="n">t</span><span class="p">:</span> <span class="n">CallableType</span><span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">object</span><span class="p">]:</span>
|
||
<span class="n">return_type_arg</span> <span class="o">=</span> <span class="p">(</span>
|
||
<span class="n">typing</span><span class="o">.</span><span class="n">Awaitable</span><span class="p">[</span><span class="n">t</span><span class="o">.</span><span class="n">return_type</span><span class="p">]</span>
|
||
<span class="k">if</span> <span class="n">t</span><span class="o">.</span><span class="n">is_async</span>
|
||
<span class="k">else</span> <span class="n">t</span><span class="o">.</span><span class="n">return_type</span>
|
||
<span class="p">)</span>
|
||
<span class="n">arguments</span> <span class="o">=</span> <span class="n">t</span><span class="o">.</span><span class="n">arguments</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">arguments</span><span class="p">,</span> <span class="bp">Ellipsis</span><span class="p">):</span>
|
||
<span class="n">argument_args</span> <span class="o">=</span> <span class="p">(</span><span class="bp">Ellipsis</span><span class="p">,)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">argument_args</span> <span class="o">=</span> <span class="p">(</span><span class="n">arg</span><span class="o">.</span><span class="n">annotation</span> <span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">arguments</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="p">(</span>
|
||
<span class="o">*</span><span class="n">arguments_args</span><span class="p">,</span>
|
||
<span class="n">return_type_arg</span>
|
||
<span class="p">)</span>
|
||
|
||
<span class="k">def</span> <span class="nf">get_parameters</span><span class="p">(</span><span class="n">t</span><span class="p">:</span> <span class="n">CallableType</span><span class="p">)</span> <span class="o">-></span> <span class="nb">tuple</span><span class="p">[</span><span class="nb">object</span><span class="p">]:</span>
|
||
<span class="n">out</span> <span class="o">=</span> <span class="p">[]</span>
|
||
<span class="k">for</span> <span class="n">arg</span> <span class="ow">in</span> <span class="n">get_args</span><span class="p">(</span><span class="n">t</span><span class="p">):</span>
|
||
<span class="k">if</span> <span class="nb">isinstance</span><span class="p">(</span><span class="n">arg</span><span class="p">,</span> <span class="n">typing</span><span class="o">.</span><span class="n">ParamSpec</span><span class="p">):</span>
|
||
<span class="n">out</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">t</span><span class="p">)</span>
|
||
<span class="k">else</span><span class="p">:</span>
|
||
<span class="n">out</span><span class="o">.</span><span class="n">extend</span><span class="p">(</span><span class="n">arg</span><span class="o">.</span><span class="n">__parameters__</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="nb">tuple</span><span class="p">(</span><span class="n">out</span><span class="p">)</span>
|
||
</pre></div>
|
||
</div>
|
||
</section>
|
||
<section id="additional-behaviors-of-types-callabletype">
|
||
<h4><a class="toc-backref" href="#additional-behaviors-of-types-callabletype" role="doc-backlink">Additional Behaviors of <code class="docutils literal notranslate"><span class="pre">types.CallableType</span></code></a></h4>
|
||
<p>As with the <code class="docutils literal notranslate"><span class="pre">A</span> <span class="pre">|</span> <span class="pre">B</span></code> syntax for unions introduced in <a class="pep reference internal" href="../pep-0604/" title="PEP 604 – Allow writing union types as X | Y">PEP 604</a>:</p>
|
||
<ul class="simple">
|
||
<li>The <code class="docutils literal notranslate"><span class="pre">__eq__</span></code> method should treat equivalent <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>
|
||
values as equal to values constructed using the builtin syntax, and
|
||
otherwise should behave like the <code class="docutils literal notranslate"><span class="pre">__eq__</span></code> of <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>.</li>
|
||
<li>The <code class="docutils literal notranslate"><span class="pre">__repr__</span></code> method should produce an arrow syntax representation that,
|
||
when evaluated, gives us back an equal <code class="docutils literal notranslate"><span class="pre">types.CallableType</span></code> instance.</li>
|
||
</ul>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="rejected-alternatives">
|
||
<h2><a class="toc-backref" href="#rejected-alternatives" role="doc-backlink">Rejected Alternatives</a></h2>
|
||
<p>Many of the alternatives we considered would have been more expressive
|
||
than <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>, for example adding support for describing
|
||
signatures that include named, optional, and variadic arguments.</p>
|
||
<p>To determine which features we most needed to support with a callable
|
||
type syntax, we did an extensive analysis of existing projects:</p>
|
||
<ul class="simple">
|
||
<li><a class="reference external" href="https://github.com/pradeep90/annotation_collector#typed-projects---callable-type">stats on the use of the Callable type</a>;</li>
|
||
<li><a class="reference external" href="https://github.com/pradeep90/annotation_collector#typed-projects---callback-usage">stats on how untyped and partially-typed callbacks are actually used</a>.</li>
|
||
</ul>
|
||
<p>We decided on a simple proposal with improved syntax for the existing
|
||
<code class="docutils literal notranslate"><span class="pre">Callable</span></code> type because the vast majority of callbacks can be correctly
|
||
described by the existing <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code> semantics:</p>
|
||
<ul class="simple">
|
||
<li>Positional parameters: By far the most important case to handle well
|
||
is simple callable types with positional parameters, such as
|
||
<code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">str)</span> <span class="pre">-></span> <span class="pre">bool</span></code></li>
|
||
<li>ParamSpec and Concatenate: The next most important feature is good
|
||
support for <a class="pep reference internal" href="../pep-0612/" title="PEP 612 – Parameter Specification Variables">PEP 612</a> <code class="docutils literal notranslate"><span class="pre">ParamSpec</span></code> and <code class="docutils literal notranslate"><span class="pre">Concatenate</span></code> types like
|
||
<code class="docutils literal notranslate"><span class="pre">(**P)</span> <span class="pre">-></span> <span class="pre">bool</span></code> and <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">**P)</span> <span class="pre">-></span> <span class="pre">bool</span></code>. These are common
|
||
primarily because of the heavy use of decorator patterns in python
|
||
code.</li>
|
||
<li>TypeVarTuples: The next most important feature, assuming <a class="pep reference internal" href="../pep-0646/" title="PEP 646 – Variadic Generics">PEP 646</a> is
|
||
accepted, is for unpacked types which are common because of cases
|
||
where a wrapper passes along <code class="docutils literal notranslate"><span class="pre">*args</span></code> to some other function.</li>
|
||
</ul>
|
||
<p>Features that other, more complicated proposals would support account
|
||
for fewer than 2% of the use cases we found. These are already
|
||
expressible using callback protocols, and since they are uncommon we
|
||
decided that it made more sense to move forward with a simpler syntax.</p>
|
||
<section id="extended-syntax-supporting-named-and-optional-arguments">
|
||
<h3><a class="toc-backref" href="#extended-syntax-supporting-named-and-optional-arguments" role="doc-backlink">Extended Syntax Supporting Named and Optional Arguments</a></h3>
|
||
<p>Another alternative was for a compatible but more complex syntax that
|
||
could express everything in this PEP but also named, optional, and
|
||
variadic arguments. In this “extended” syntax proposal the following
|
||
types would have been equivalent:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Function</span><span class="p">(</span><span class="n">typing</span><span class="o">.</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="o">/</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">z</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="o">...</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">Function</span> <span class="o">=</span> <span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">z</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="o">...</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>Advantages of this syntax include: - Most of the advantages of the
|
||
proposal in this PEP (conciseness, <a class="pep reference internal" href="../pep-0612/" title="PEP 612 – Parameter Specification Variables">PEP 612</a> support, etc) -
|
||
Furthermore, the ability to handle named, optional, and variadic
|
||
arguments</p>
|
||
<p>We decided against proposing it for the following reasons:</p>
|
||
<ul>
|
||
<li>The implementation would have been more difficult, and usage stats
|
||
demonstrate that fewer than 3% of use cases would benefit from any
|
||
of the added features.</li>
|
||
<li>The group that debated these proposals was split down the middle
|
||
about whether these changes are desirable:<ul class="simple">
|
||
<li>On the one hand, they make callable types more expressive. On the
|
||
other hand, they could easily confuse users who have not read the
|
||
full specification of callable type syntax.</li>
|
||
<li>We believe the simpler syntax proposed in this PEP, which
|
||
introduces no new semantics and closely mimics syntax in other
|
||
popular languages like Kotlin, Scala, and TypesScript, is much
|
||
less likely to confuse users.</li>
|
||
</ul>
|
||
</li>
|
||
<li>We intend to implement the current proposal in a way that is
|
||
forward-compatible with the more complicated extended syntax. If the
|
||
community decides after more experience and discussion that we want
|
||
the additional features, it should be straightforward to propose
|
||
them in the future.</li>
|
||
<li>Even a full extended syntax cannot replace the use of callback
|
||
protocols for overloads. For example, no closed form of callable type
|
||
could express a function that maps bools to bools and ints to floats,
|
||
like this callback protocol.:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="kn">from</span> <span class="nn">typing</span> <span class="kn">import</span> <span class="n">overload</span><span class="p">,</span> <span class="n">Protocol</span>
|
||
|
||
<span class="k">class</span> <span class="nc">OverloadedCallback</span><span class="p">(</span><span class="n">Protocol</span><span class="p">)</span>
|
||
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="nd">@overload</span>
|
||
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">bool</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="k">def</span> <span class="fm">__call__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span> <span class="o">|</span> <span class="nb">bool</span><span class="p">)</span> <span class="o">-></span> <span class="nb">float</span> <span class="o">|</span> <span class="nb">bool</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
|
||
<span class="n">f</span><span class="p">:</span> <span class="n">OverloadedCallback</span> <span class="o">=</span> <span class="o">...</span>
|
||
<span class="n">f</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span> <span class="c1"># bool</span>
|
||
<span class="n">f</span><span class="p">(</span><span class="mi">3</span><span class="p">)</span> <span class="c1"># float</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
<p>We confirmed that the current proposal is forward-compatible with
|
||
extended syntax by
|
||
<a class="reference external" href="https://github.com/stroxler/cpython/tree/callable-type-syntax--extended">implementing</a>
|
||
a grammar and AST for this extended syntax on top of our reference
|
||
implementation of this PEP’s grammar.</p>
|
||
</section>
|
||
<section id="syntax-closer-to-function-signatures">
|
||
<h3><a class="toc-backref" href="#syntax-closer-to-function-signatures" role="doc-backlink">Syntax Closer to Function Signatures</a></h3>
|
||
<p>One alternative we had floated was a syntax much more similar to
|
||
function signatures.</p>
|
||
<p>In this proposal, the following types would have been equivalent:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">class</span> <span class="nc">Function</span><span class="p">(</span><span class="n">typing</span><span class="o">.</span><span class="n">Protocol</span><span class="p">):</span>
|
||
<span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="o">/</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">z</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="o">...</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span>
|
||
<span class="o">...</span>
|
||
|
||
<span class="n">Function</span> <span class="o">=</span> <span class="p">(</span><span class="n">x</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span> <span class="o">/</span><span class="p">,</span> <span class="n">y</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span> <span class="o">*</span><span class="p">,</span> <span class="n">z</span><span class="p">:</span> <span class="nb">bool</span> <span class="o">=</span> <span class="o">...</span><span class="p">,</span> <span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>The benefits of this proposal would have included:</p>
|
||
<ul class="simple">
|
||
<li>Perfect syntactic consistency between signatures and callable types.</li>
|
||
<li>Support for more features of function signatures (named, optional,
|
||
variadic args) that this PEP does not support.</li>
|
||
</ul>
|
||
<p>Key downsides that led us to reject the idea include the following:</p>
|
||
<ul class="simple">
|
||
<li>A large majority of use cases only use positional-only arguments. This
|
||
syntax would be more verbose for that use case, both because of requiring
|
||
argument names and an explicit <code class="docutils literal notranslate"><span class="pre">/</span></code>, for example <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">/)</span> <span class="pre">-></span> <span class="pre">bool</span></code> where
|
||
our proposal allows <code class="docutils literal notranslate"><span class="pre">(int)</span> <span class="pre">-></span> <span class="pre">bool</span></code></li>
|
||
<li>The requirement for explicit <code class="docutils literal notranslate"><span class="pre">/</span></code> for positional-only arguments has
|
||
a high risk of causing frequent bugs - which often would not be
|
||
detected by unit tests - where library authors would accidentally
|
||
use types with named arguments.</li>
|
||
<li>Our analysis suggests that support for <code class="docutils literal notranslate"><span class="pre">ParamSpec</span></code> is key, but the
|
||
scoping rules laid out in <a class="pep reference internal" href="../pep-0612/" title="PEP 612 – Parameter Specification Variables">PEP 612</a> would have made this difficult.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="other-proposals-considered">
|
||
<h3><a class="toc-backref" href="#other-proposals-considered" role="doc-backlink">Other Proposals Considered</a></h3>
|
||
<section id="functions-as-types">
|
||
<h4><a class="toc-backref" href="#functions-as-types" role="doc-backlink">Functions-as-Types</a></h4>
|
||
<p>An idea we looked at very early on was to <a class="reference external" href="https://docs.google.com/document/d/1rv6CCDnmLIeDrYlXe-QcyT0xNPSYAuO1EBYjU3imU5s/edit?usp=sharing">allow using functions as types</a>.
|
||
The idea is allowing a function to stand in for its own call
|
||
signature, with roughly the same semantics as the <code class="docutils literal notranslate"><span class="pre">__call__</span></code> method
|
||
of callback protocols:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">CallableType</span><span class="p">(</span>
|
||
<span class="n">positional_only</span><span class="p">:</span> <span class="nb">int</span><span class="p">,</span>
|
||
<span class="o">/</span><span class="p">,</span>
|
||
<span class="n">named</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span>
|
||
<span class="o">*</span><span class="n">args</span><span class="p">:</span> <span class="nb">float</span><span class="p">,</span>
|
||
<span class="n">keyword_only</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="o">...</span><span class="p">,</span>
|
||
<span class="o">**</span><span class="n">kwargs</span><span class="p">:</span> <span class="nb">str</span>
|
||
<span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span><span class="p">:</span> <span class="o">...</span>
|
||
|
||
<span class="n">f</span><span class="p">:</span> <span class="n">CallableType</span> <span class="o">=</span> <span class="o">...</span>
|
||
<span class="n">f</span><span class="p">(</span><span class="mi">5</span><span class="p">,</span> <span class="mf">6.6</span><span class="p">,</span> <span class="mf">6.7</span><span class="p">,</span> <span class="n">named</span><span class="o">=</span><span class="mi">6</span><span class="p">,</span> <span class="n">x</span><span class="o">=</span><span class="s2">"hello"</span><span class="p">,</span> <span class="n">y</span><span class="o">=</span><span class="s2">"world"</span><span class="p">)</span> <span class="c1"># typechecks as bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This may be a good idea, but we do not consider it a viable
|
||
replacement for callable types:</p>
|
||
<ul class="simple">
|
||
<li>It would be difficult to handle <code class="docutils literal notranslate"><span class="pre">ParamSpec</span></code>, which we consider a
|
||
critical feature to support.</li>
|
||
<li>When using functions as types, the callable types are not first-class
|
||
values. Instead, they require a separate, out-of-line function
|
||
definition to define a type alias</li>
|
||
<li>It would not support more features than callback protocols, and seems
|
||
more like a shorter way to write them than a replacement for
|
||
<code class="docutils literal notranslate"><span class="pre">Callable</span></code>.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="hybrid-keyword-arrow-syntax">
|
||
<h4><a class="toc-backref" href="#hybrid-keyword-arrow-syntax" role="doc-backlink">Hybrid keyword-arrow Syntax</a></h4>
|
||
<p>In the Rust language, a keyword <code class="docutils literal notranslate"><span class="pre">fn</span></code> is used to indicate functions
|
||
in much the same way as Python’s <code class="docutils literal notranslate"><span class="pre">def</span></code>, and callable types are
|
||
indicated using a hybrid arrow syntax <code class="docutils literal notranslate"><span class="pre">Fn(i64,</span> <span class="pre">String)</span> <span class="pre">-></span> <span class="pre">bool</span></code>.</p>
|
||
<p>We could use the <code class="docutils literal notranslate"><span class="pre">def</span></code> keyword in callable types for Python, for
|
||
example our two-parameter boolean function could be written as
|
||
<code class="docutils literal notranslate"><span class="pre">def(int,</span> <span class="pre">str)</span> <span class="pre">-></span> <span class="pre">bool</span></code>. But we think this might confuse readers
|
||
into thinking <code class="docutils literal notranslate"><span class="pre">def(A,</span> <span class="pre">B)</span> <span class="pre">-></span> <span class="pre">C</span></code> is a lambda, particularly because
|
||
Javascript’s <code class="docutils literal notranslate"><span class="pre">function</span></code> keyword is used in both named and anonymous
|
||
functions.</p>
|
||
</section>
|
||
<section id="parenthesis-free-syntax">
|
||
<h4><a class="toc-backref" href="#parenthesis-free-syntax" role="doc-backlink">Parenthesis-Free Syntax</a></h4>
|
||
<p>We considered a parentheses-free syntax that would have been even more
|
||
concise:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span> <span class="o">-></span> <span class="nb">bool</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We decided against it because this is not visually as similar to
|
||
existing function header syntax. Moreover, it is visually similar to
|
||
lambdas, which bind names with no parentheses: <code class="docutils literal notranslate"><span class="pre">lambda</span> <span class="pre">x,</span> <span class="pre">y:</span> <span class="pre">x</span> <span class="pre">==</span>
|
||
<span class="pre">y</span></code>.</p>
|
||
</section>
|
||
<section id="requiring-outer-parentheses">
|
||
<h4><a class="toc-backref" href="#requiring-outer-parentheses" role="doc-backlink">Requiring Outer Parentheses</a></h4>
|
||
<p>A concern with the current proposal is readability, particularly
|
||
when callable types are used in return type position which leads to
|
||
multiple top-level <code class="docutils literal notranslate"><span class="pre">-></span></code> tokens, for example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">make_adder</span><span class="p">()</span> <span class="o">-></span> <span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We considered a few ideas to prevent this by changing rules about
|
||
parentheses. One was to move the parentheses to the outside, so
|
||
that a two-argument boolean function is written <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">str</span> <span class="pre">-></span> <span class="pre">bool)</span></code>.
|
||
With this change, the example above becomes:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">make_adder</span><span class="p">()</span> <span class="o">-></span> <span class="p">(</span><span class="nb">int</span> <span class="o">-></span> <span class="nb">int</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This makes the nesting of many examples that are difficult to
|
||
follow clear, but we rejected it because</p>
|
||
<ul class="simple">
|
||
<li>Currently in Python commas bind very loosely, which means it might be common
|
||
to misread <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">str</span> <span class="pre">-></span> <span class="pre">bool)</span></code> as a tuple whose first element is an int,
|
||
rather than a two-parameter callable type.</li>
|
||
<li>It is not very similar to function header syntax, and one of our goals was
|
||
familiar syntax inspired by function headers.</li>
|
||
<li>This syntax may be more readable for deaply nested callables like the one
|
||
above, but deep nesting is not very common. Encouraging extra parentheses
|
||
around callable types in return position via a style guide would have most of
|
||
the readability benefit without the downsides.</li>
|
||
</ul>
|
||
<p>We also considered requiring parentheses on both the parameter list and the
|
||
outside, e.g. <code class="docutils literal notranslate"><span class="pre">((int,</span> <span class="pre">str)</span> <span class="pre">-></span> <span class="pre">bool)</span></code>. With this change, the example above
|
||
becomes:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">make_adder</span><span class="p">()</span> <span class="o">-></span> <span class="p">((</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span><span class="p">):</span>
|
||
<span class="k">return</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We rejected this change because:</p>
|
||
<ul>
|
||
<li>The outer parentheses only help readability in some cases, mostly when a
|
||
callable type is used in return position. In many other cases they hurt
|
||
readability rather than helping.</li>
|
||
<li>We agree that it might make sense to encourage outer parentheses in several
|
||
cases, particularly callable types in function return annotations. But<ul>
|
||
<li>We believe it is more appropriate to encourage this in style guides,
|
||
linters, and autoformatters than to bake it into the parser and throw
|
||
syntax errors.</li>
|
||
<li>Moreover, if a type is complicated enough that readability is a concern
|
||
we can always use type aliases, for example:<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="n">IntToIntFunction</span><span class="p">:</span> <span class="p">(</span><span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">int</span>
|
||
|
||
<span class="k">def</span> <span class="nf">make_adder</span><span class="p">()</span> <span class="o">-></span> <span class="n">IntToIntFunction</span><span class="p">:</span>
|
||
<span class="k">return</span> <span class="k">lambda</span> <span class="n">x</span><span class="p">:</span> <span class="n">x</span> <span class="o">+</span> <span class="mi">1</span>
|
||
</pre></div>
|
||
</div>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</section>
|
||
<section id="making-bind-tighter-than">
|
||
<h4><a class="toc-backref" href="#making-bind-tighter-than" role="doc-backlink">Making <code class="docutils literal notranslate"><span class="pre">-></span></code> bind tighter than <code class="docutils literal notranslate"><span class="pre">|</span></code></a></h4>
|
||
<p>In order to allow both <code class="docutils literal notranslate"><span class="pre">-></span></code> and <code class="docutils literal notranslate"><span class="pre">|</span></code> tokens in type expressions we
|
||
had to choose precedence. In the current proposal, this is a function
|
||
returning an optional boolean:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-></span> <span class="nb">bool</span> <span class="o">|</span> <span class="kc">None</span> <span class="c1"># equivalent to (int, str) -> (bool | None)</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>We considered having <code class="docutils literal notranslate"><span class="pre">-></span></code> bind tighter so that instead the expression
|
||
would parse as <code class="docutils literal notranslate"><span class="pre">((int,</span> <span class="pre">str)</span> <span class="pre">-></span> <span class="pre">bool)</span> <span class="pre">|</span> <span class="pre">None</span></code>. There are two advantages
|
||
to this:</p>
|
||
<ul class="simple">
|
||
<li>It means we no would longer have to treat <code class="docutils literal notranslate"><span class="pre">None</span> <span class="pre">|</span> <span class="pre">(int,</span> <span class="pre">str)</span> <span class="pre">-></span>
|
||
<span class="pre">bool</span></code> as a syntax error.</li>
|
||
<li>Looking at typeshed today, optional callable arguments are very common
|
||
because using <code class="docutils literal notranslate"><span class="pre">None</span></code> as a default value is a standard Python idiom.
|
||
Having <code class="docutils literal notranslate"><span class="pre">-></span></code> bind tighter would make these easier to write.</li>
|
||
</ul>
|
||
<p>We decided against this for a few reasons:</p>
|
||
<ul class="simple">
|
||
<li>The function header <code class="docutils literal notranslate"><span class="pre">def</span> <span class="pre">f()</span> <span class="pre">-></span> <span class="pre">int</span> <span class="pre">|</span> <span class="pre">None:</span> <span class="pre">...</span></code> is legal
|
||
and indicates a function returning an optional int. To be consistent
|
||
with function headers, callable types should do the same.</li>
|
||
<li>TypeScript is the other popular language we know of that uses both
|
||
<code class="docutils literal notranslate"><span class="pre">-></span></code> and <code class="docutils literal notranslate"><span class="pre">|</span></code> tokens in type expressions, and they have <code class="docutils literal notranslate"><span class="pre">|</span></code> bind
|
||
tighter. While we do not have to follow their lead, we prefer to do
|
||
so.</li>
|
||
<li>We do acknowledge that optional callable types are common and
|
||
having <code class="docutils literal notranslate"><span class="pre">|</span></code> bind tighter forces extra parentheses, which makes these
|
||
types harder to write. But code is read more often than written, and
|
||
we believe that requiring the outer parentheses for an optional callable
|
||
type like <code class="docutils literal notranslate"><span class="pre">((int,</span> <span class="pre">str)</span> <span class="pre">-></span> <span class="pre">bool)</span> <span class="pre">|</span> <span class="pre">None</span></code> is preferable for readability.</li>
|
||
</ul>
|
||
</section>
|
||
<section id="introducing-type-strings">
|
||
<h4><a class="toc-backref" href="#introducing-type-strings" role="doc-backlink">Introducing type-strings</a></h4>
|
||
<p>Another idea was adding a new “special string” syntax and putting the type
|
||
inside of it, for example <code class="docutils literal notranslate"><span class="pre">t”(int,</span> <span class="pre">str)</span> <span class="pre">-></span> <span class="pre">bool”</span></code>. We rejected this
|
||
because it is not as readable, and seems out of step with <a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/message/SZLWVYV2HPLU6AH7DOUD7DWFUGBJGQAY/">guidance</a>
|
||
from the Steering Council on ensuring that type expressions do not
|
||
diverge from the rest of Python’s syntax.</p>
|
||
</section>
|
||
<section id="improving-usability-of-the-indexed-callable-type">
|
||
<h4><a class="toc-backref" href="#improving-usability-of-the-indexed-callable-type" role="doc-backlink">Improving Usability of the Indexed Callable Type</a></h4>
|
||
<p>If we do not want to add new syntax for callable types, we could
|
||
look at how to make the existing type easier to read. One proposal
|
||
would be to make the builtin <code class="docutils literal notranslate"><span class="pre">callable</span></code> function indexable so
|
||
that it could be used as a type:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">callable</span><span class="p">[[</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">],</span> <span class="nb">bool</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This change would be analogous to <a class="pep reference internal" href="../pep-0585/" title="PEP 585 – Type Hinting Generics In Standard Collections">PEP 585</a> that made built in collections
|
||
like <code class="docutils literal notranslate"><span class="pre">list</span></code> and <code class="docutils literal notranslate"><span class="pre">dict</span></code> usable as types, and would make imports
|
||
more convenient, but it wouldn’t help readability of the types themselves
|
||
much.</p>
|
||
<p>In order to reduce the number of brackets needed in complex callable
|
||
types, it would be possible to allow tuples for the argument list:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="nb">callable</span><span class="p">[(</span><span class="nb">int</span><span class="p">,</span> <span class="nb">str</span><span class="p">),</span> <span class="nb">bool</span><span class="p">]</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>This actually is a significant readability improvement for
|
||
multi-argument functions, but the problem is that it makes callables
|
||
with one arguments, which are the most common arity, hard to
|
||
write: because <code class="docutils literal notranslate"><span class="pre">(x)</span></code> evaluates to <code class="docutils literal notranslate"><span class="pre">x</span></code>, they would have to be
|
||
written like <code class="docutils literal notranslate"><span class="pre">callable[(int,),</span> <span class="pre">bool]</span></code>. We find this awkward.</p>
|
||
<p>Moreover, none of these ideas help as much with reducing verbosity
|
||
as the current proposal, nor do they introduce as strong a visual cue
|
||
as the <code class="docutils literal notranslate"><span class="pre">-></span></code> between the parameter types and the return type.</p>
|
||
</section>
|
||
</section>
|
||
<section id="alternative-runtime-behaviors">
|
||
<h3><a class="toc-backref" href="#alternative-runtime-behaviors" role="doc-backlink">Alternative Runtime Behaviors</a></h3>
|
||
<p>The hard requirements on our runtime API are that:</p>
|
||
<ul class="simple">
|
||
<li>It must preserve backward compatibility with <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code> via
|
||
<code class="docutils literal notranslate"><span class="pre">__args__</span></code> and <code class="docutils literal notranslate"><span class="pre">__params__</span></code>.</li>
|
||
<li>It must provide a structured API, which should be extensible if
|
||
in the future we try to support named and variadic arguments.</li>
|
||
</ul>
|
||
<section id="alternative-apis">
|
||
<h4><a class="toc-backref" href="#alternative-apis" role="doc-backlink">Alternative APIs</a></h4>
|
||
<p>We considered having the runtime data <code class="docutils literal notranslate"><span class="pre">types.CallableType</span></code> use a
|
||
more structured API where there would be separate fields for
|
||
<code class="docutils literal notranslate"><span class="pre">posonlyargs</span></code> and <code class="docutils literal notranslate"><span class="pre">param_spec</span></code>. The current proposal was
|
||
was inspired by the <code class="docutils literal notranslate"><span class="pre">inspect.Signature</span></code> type.</p>
|
||
<p>We use “argument” in our field and type names, unlike “parameter”
|
||
as in <code class="docutils literal notranslate"><span class="pre">inspect.Signature</span></code>, in order to avoid confusion with
|
||
the <code class="docutils literal notranslate"><span class="pre">callable_type.__parameters__</span></code> field from the legacy API
|
||
that refers to type parameters rather than callable parameters.</p>
|
||
</section>
|
||
<section id="using-the-plain-return-type-in-args-for-async-types">
|
||
<h4><a class="toc-backref" href="#using-the-plain-return-type-in-args-for-async-types" role="doc-backlink">Using the plain return type in <code class="docutils literal notranslate"><span class="pre">__args__</span></code> for async types</a></h4>
|
||
<p>It is debatable whether we are required to preserve backward compatibility
|
||
of <code class="docutils literal notranslate"><span class="pre">__args__</span></code> for async callable types like <code class="docutils literal notranslate"><span class="pre">async</span> <span class="pre">(int)</span> <span class="pre">-></span> <span class="pre">str</span></code>. The
|
||
reason is that one could argue they are not expressible directly
|
||
using <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>, and therefore it would be fine to set
|
||
<code class="docutils literal notranslate"><span class="pre">__args__</span></code> as <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">int)</span></code> rather than <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">typing.Awaitable[int])</span></code>.</p>
|
||
<p>But we believe this would be problematic. By preserving the appearance
|
||
of a backward-compatible API while actually breaking its semantics on
|
||
async types, we would cause runtime type libraries that attempt to
|
||
interpret <code class="docutils literal notranslate"><span class="pre">Callable</span></code> using <code class="docutils literal notranslate"><span class="pre">__args__</span></code> to fail silently.</p>
|
||
<p>It is for this reason that we automatically wrap the return type in
|
||
<code class="docutils literal notranslate"><span class="pre">Awaitable</span></code>.</p>
|
||
</section>
|
||
</section>
|
||
</section>
|
||
<section id="backward-compatibility">
|
||
<h2><a class="toc-backref" href="#backward-compatibility" role="doc-backlink">Backward Compatibility</a></h2>
|
||
<p>This PEP proposes a major syntax improvement over <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code>,
|
||
but the static semantics are the same.</p>
|
||
<p>As such, the only thing we need for backward compatibility is to
|
||
ensure that types specified via the new syntax behave the same as
|
||
equivalent <code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code> and <code class="docutils literal notranslate"><span class="pre">typing.Concatenate</span></code> values they
|
||
intend to replace.</p>
|
||
<p>There is no particular interaction between this proposal and <code class="docutils literal notranslate"><span class="pre">from</span>
|
||
<span class="pre">__future__</span> <span class="pre">import</span> <span class="pre">annotations</span></code> - just like any other type annotation
|
||
it will be unparsed to a string at module import, and
|
||
<code class="docutils literal notranslate"><span class="pre">typing.get_type_hints</span></code> should correctly evaluate the resulting
|
||
strings in cases where that is possible.</p>
|
||
<p>This is discussed in more detail in the Runtime Behavior section.</p>
|
||
</section>
|
||
<section id="reference-implementation">
|
||
<h2><a class="toc-backref" href="#reference-implementation" role="doc-backlink">Reference Implementation</a></h2>
|
||
<p>We have a working <a class="reference external" href="https://github.com/stroxler/cpython/tree/callable-type-syntax--shorthand">implementation</a>
|
||
of the AST and Grammar with tests verifying that the grammar proposed
|
||
here has the desired behaviors.</p>
|
||
<p>The runtime behavior is not yet implemented. As discussed in the
|
||
<a class="reference internal" href="#runtime-behavior">Runtime Behavior</a> portion of the spec we have a detailed plan for
|
||
both a backward-compatible API and a more structured API in
|
||
<a class="reference external" href="https://docs.google.com/document/d/15nmTDA_39Lo-EULQQwdwYx_Q1IYX4dD5WPnHbFG71Lk/edit">a separate doc</a>
|
||
where we are also open to discussion and alternative ideas.</p>
|
||
</section>
|
||
<section id="open-issues">
|
||
<h2><a class="toc-backref" href="#open-issues" role="doc-backlink">Open Issues</a></h2>
|
||
<section id="details-of-the-runtime-api">
|
||
<h3><a class="toc-backref" href="#details-of-the-runtime-api" role="doc-backlink">Details of the Runtime API</a></h3>
|
||
<p>We have attempted to provide a complete behavior specification in
|
||
the <a class="reference internal" href="#runtime-behavior">Runtime Behavior</a> section of this PEP.</p>
|
||
<p>But there are probably more details that we will not realize we
|
||
need to define until we build a full reference implementation.</p>
|
||
</section>
|
||
<section id="optimizing-syntaxerror-messages">
|
||
<h3><a class="toc-backref" href="#optimizing-syntaxerror-messages" role="doc-backlink">Optimizing <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> messages</a></h3>
|
||
<p>The current reference implementation has a fully-functional parser and
|
||
all edge cases presented here have been tested.</p>
|
||
<p>But there are some known cases where the errors are not as informative
|
||
as we would like. For example, because <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">...)</span> <span class="pre">-></span> <span class="pre">bool</span></code> is
|
||
illegal but <code class="docutils literal notranslate"><span class="pre">(int,</span> <span class="pre">...)</span></code> is a valid tuple, we currently produce a
|
||
syntax error flagging the <code class="docutils literal notranslate"><span class="pre">-></span></code> as the problem even though the real
|
||
cause of the error is using <code class="docutils literal notranslate"><span class="pre">...</span></code> as an argument type.</p>
|
||
<p>This is not part of the specification <em>per se</em> but is an important
|
||
detail to address in our implementation. The solution will likely
|
||
involve adding <code class="docutils literal notranslate"><span class="pre">invalid_.*</span></code> rules to <code class="docutils literal notranslate"><span class="pre">python.gram</span></code> and customizing
|
||
error messages.</p>
|
||
</section>
|
||
</section>
|
||
<section id="resources">
|
||
<h2><a class="toc-backref" href="#resources" role="doc-backlink">Resources</a></h2>
|
||
<section id="background-and-history">
|
||
<h3><a class="toc-backref" href="#background-and-history" role="doc-backlink">Background and History</a></h3>
|
||
<p><a class="pep reference internal" href="../pep-0484/#suggested-syntax-for-python-2-7-and-straddling-code" title="PEP 484 – Type Hints § Suggested syntax for Python 2.7 and straddling code">PEP 484 specifies</a>
|
||
a very similar syntax for function type hint <em>comments</em> for use in
|
||
code that needs to work on Python 2.7. For example:</p>
|
||
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="k">def</span> <span class="nf">f</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
|
||
<span class="c1"># type: (int, str) -> bool</span>
|
||
<span class="o">...</span>
|
||
</pre></div>
|
||
</div>
|
||
<p>At that time we used indexing operations to specify generic types like
|
||
<code class="docutils literal notranslate"><span class="pre">typing.Callable</span></code> because we decided not to add syntax for
|
||
types. However, we have since begun to do so, e.g. with <a class="pep reference internal" href="../pep-0604/" title="PEP 604 – Allow writing union types as X | Y">PEP 604</a>.</p>
|
||
<p><strong>Maggie</strong> proposed better callable type syntax as part of a larger
|
||
<a class="reference external" href="https://drive.google.com/file/d/1XhqTKoO6RHtz7zXqW5Wgq9nzaEz9TXjI/view">presentation on typing simplifications</a>
|
||
at the PyCon Typing Summit 2021.</p>
|
||
<p><strong>Steven</strong> <a class="reference external" href="https://mail.python.org/archives/list/typing-sig@python.org/thread/3JNXLYH5VFPBNIVKT6FFBVVFCZO4GFR2">brought up this proposal on typing-sig</a>.
|
||
We had several meetings to discuss alternatives, and <a class="reference external" href="https://www.dropbox.com/s/sshgtr4p30cs0vc/Python%20Callable%20Syntax%20Proposals.pdf?dl=0">this presentation</a>
|
||
led us to the current proposal.</p>
|
||
<p><strong>Pradeep</strong> <a class="reference external" href="https://mail.python.org/archives/list/python-dev@python.org/thread/VBHJOS3LOXGVU6I4FABM6DKHH65GGCUB">brought this proposal to python-dev</a>
|
||
for feedback.</p>
|
||
</section>
|
||
<section id="acknowledgments">
|
||
<h3><a class="toc-backref" href="#acknowledgments" role="doc-backlink">Acknowledgments</a></h3>
|
||
<p>Thanks to the following people for their feedback on the PEP and help
|
||
planning the reference implementation:</p>
|
||
<p>Alex Waygood, Eric Traut, Guido van Rossum, James Hilton-Balfe,
|
||
Jelle Zijlstra, Maggie Moss, Tuomas Suutari, Shannon Zhu.</p>
|
||
</section>
|
||
</section>
|
||
<section id="copyright">
|
||
<h2><a class="toc-backref" href="#copyright" role="doc-backlink">Copyright</a></h2>
|
||
<p>This document is placed in the public domain or under the
|
||
CC0-1.0-Universal license, whichever is more permissive.</p>
|
||
</section>
|
||
</section>
|
||
<hr class="docutils" />
|
||
<p>Source: <a class="reference external" href="https://github.com/python/peps/blob/main/peps/pep-0677.rst">https://github.com/python/peps/blob/main/peps/pep-0677.rst</a></p>
|
||
<p>Last modified: <a class="reference external" href="https://github.com/python/peps/commits/main/peps/pep-0677.rst">2023-09-09 17:39:29 GMT</a></p>
|
||
|
||
</article>
|
||
<nav id="pep-sidebar">
|
||
<h2>Contents</h2>
|
||
<ul>
|
||
<li><a class="reference internal" href="#abstract">Abstract</a></li>
|
||
<li><a class="reference internal" href="#motivation">Motivation</a></li>
|
||
<li><a class="reference internal" href="#rationale">Rationale</a><ul>
|
||
<li><a class="reference internal" href="#an-arrow-syntax-for-callable-types">An Arrow Syntax for Callable Types</a></li>
|
||
<li><a class="reference internal" href="#compact-syntax-for-paramspec">Compact Syntax for <code class="docutils literal notranslate"><span class="pre">ParamSpec</span></code></a></li>
|
||
<li><a class="reference internal" href="#comparing-to-other-languages">Comparing to Other Languages</a><ul>
|
||
<li><a class="reference internal" href="#id1">TypeScript</a></li>
|
||
<li><a class="reference internal" href="#id3">Kotlin</a></li>
|
||
<li><a class="reference internal" href="#id5">Scala</a></li>
|
||
<li><a class="reference internal" href="#function-definitions-vs-callable-type-annotations">Function Definitions vs Callable Type Annotations</a></li>
|
||
<li><a class="reference internal" href="#the-ml-language-family">The ML Language Family</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#specification">Specification</a><ul>
|
||
<li><a class="reference internal" href="#typing-behavior">Typing Behavior</a></li>
|
||
<li><a class="reference internal" href="#grammar-and-ast">Grammar and AST</a></li>
|
||
<li><a class="reference internal" href="#implications-of-the-grammar">Implications of the Grammar</a><ul>
|
||
<li><a class="reference internal" href="#precedence-of">Precedence of -></a></li>
|
||
<li><a class="reference internal" href="#async-keyword"><code class="docutils literal notranslate"><span class="pre">async</span></code> Keyword</a></li>
|
||
<li><a class="reference internal" href="#trailing-commas">Trailing Commas</a></li>
|
||
<li><a class="reference internal" href="#disallowing-as-an-argument-type">Disallowing <code class="docutils literal notranslate"><span class="pre">...</span></code> as an Argument Type</a></li>
|
||
<li><a class="reference internal" href="#incompatibility-with-other-possible-uses-of-and">Incompatibility with other possible uses of <code class="docutils literal notranslate"><span class="pre">*</span></code> and <code class="docutils literal notranslate"><span class="pre">**</span></code></a></li>
|
||
<li><a class="reference internal" href="#compatibility-with-arrow-based-lambda-syntax">Compatibility with Arrow-Based Lambda Syntax</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#runtime-behavior">Runtime Behavior</a><ul>
|
||
<li><a class="reference internal" href="#evaluation-and-structured-api">Evaluation and Structured API</a></li>
|
||
<li><a class="reference internal" href="#backward-compatible-api">Backward-Compatible API</a></li>
|
||
<li><a class="reference internal" href="#additional-behaviors-of-types-callabletype">Additional Behaviors of <code class="docutils literal notranslate"><span class="pre">types.CallableType</span></code></a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#rejected-alternatives">Rejected Alternatives</a><ul>
|
||
<li><a class="reference internal" href="#extended-syntax-supporting-named-and-optional-arguments">Extended Syntax Supporting Named and Optional Arguments</a></li>
|
||
<li><a class="reference internal" href="#syntax-closer-to-function-signatures">Syntax Closer to Function Signatures</a></li>
|
||
<li><a class="reference internal" href="#other-proposals-considered">Other Proposals Considered</a><ul>
|
||
<li><a class="reference internal" href="#functions-as-types">Functions-as-Types</a></li>
|
||
<li><a class="reference internal" href="#hybrid-keyword-arrow-syntax">Hybrid keyword-arrow Syntax</a></li>
|
||
<li><a class="reference internal" href="#parenthesis-free-syntax">Parenthesis-Free Syntax</a></li>
|
||
<li><a class="reference internal" href="#requiring-outer-parentheses">Requiring Outer Parentheses</a></li>
|
||
<li><a class="reference internal" href="#making-bind-tighter-than">Making <code class="docutils literal notranslate"><span class="pre">-></span></code> bind tighter than <code class="docutils literal notranslate"><span class="pre">|</span></code></a></li>
|
||
<li><a class="reference internal" href="#introducing-type-strings">Introducing type-strings</a></li>
|
||
<li><a class="reference internal" href="#improving-usability-of-the-indexed-callable-type">Improving Usability of the Indexed Callable Type</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#alternative-runtime-behaviors">Alternative Runtime Behaviors</a><ul>
|
||
<li><a class="reference internal" href="#alternative-apis">Alternative APIs</a></li>
|
||
<li><a class="reference internal" href="#using-the-plain-return-type-in-args-for-async-types">Using the plain return type in <code class="docutils literal notranslate"><span class="pre">__args__</span></code> for async types</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#backward-compatibility">Backward Compatibility</a></li>
|
||
<li><a class="reference internal" href="#reference-implementation">Reference Implementation</a></li>
|
||
<li><a class="reference internal" href="#open-issues">Open Issues</a><ul>
|
||
<li><a class="reference internal" href="#details-of-the-runtime-api">Details of the Runtime API</a></li>
|
||
<li><a class="reference internal" href="#optimizing-syntaxerror-messages">Optimizing <code class="docutils literal notranslate"><span class="pre">SyntaxError</span></code> messages</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#resources">Resources</a><ul>
|
||
<li><a class="reference internal" href="#background-and-history">Background and History</a></li>
|
||
<li><a class="reference internal" href="#acknowledgments">Acknowledgments</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a class="reference internal" href="#copyright">Copyright</a></li>
|
||
</ul>
|
||
|
||
<br>
|
||
<a id="source" href="https://github.com/python/peps/blob/main/peps/pep-0677.rst">Page Source (GitHub)</a>
|
||
</nav>
|
||
</section>
|
||
<script src="../_static/colour_scheme.js"></script>
|
||
<script src="../_static/wrap_tables.js"></script>
|
||
<script src="../_static/sticky_banner.js"></script>
|
||
</body>
|
||
</html> |