<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
  <head>
    <meta content="text/html;charset=UTF-8" http-equiv="Content-Type">
    <title>Thoughts on a conservative terse syntax for constraints</title>
    <style type="text/css">
      html { margin: 0; padding: 0; color: black; background-color: white; }
      body { padding: 2em; font-size: medium; font-family: "DejaVu Serif", serif; line-height: 150%; }
      code { font-family: "DejaVu Sans Mono", monospace; color: #006; }

      h1, h2, h3 { margin: 1.5em 0 .75em 0; line-height: 125%; }

      div.code { white-space: pre-line; font-family: "DejaVu Sans Mono", monospace;
                 border: thin solid #E0E0E0; background-color: #F8F8F8; padding: 1em;
                 border-radius: 4px; line-height: 200%; }

      div.strictpre { white-space: pre; }

      sup, sub { line-height: 0; }

      .docinfo { float: right; }
      .docinfo p { margin: 0; text-align:right; }
      .docinfo address { font-style: normal; }

      .quote { display: inline-block; clear: both; margin-left: 1ex;
                 border: thin solid #E0E0E0; background-color: #F8F8F8; padding: 1ex; }

      .modify { border-left: thick solid #999; border-right: thick solid #999; padding: 0 1em; }
      .insert { border-left: thick solid #0A0; border-right: thick solid #0A0; padding: 0 1em; }
      .comment { color: #753; }
      .comment a:link, .comment a:visited { color: #A51; }

      table { border-top: 2px solid black; border-bottom: 2px solid black; border-collapse: collapse; margin: 3em auto; }
      thead th { border-bottom: 2px solid black; }
      th.sep { border-top: 2px solid black; border-bottom: 2px solid black; }
      th, td { text-align: left; padding: 0; margin: 0; }
      tr:first-child td { padding-top: 1ex; }
      tr:last-child td { padding-bottom: 1ex; }

      td.code { white-space: pre-line; font-family: "DejaVu Sans Mono", monospace;
                padding: 1em; line-height: 150%; }

      table.x { caption-side: bottom; }
      table.x caption { padding-top: 1ex; }
      .x th, .x td { padding: 1ex 1ex 1ex 1ex; }
      .x th:first-child, .x td:first-child { padding-left: 1ex; }
      td.y { padding: 0 1em; }

      p.skip { margin-top: 1em; }

      ins { color: #090; }
      del { color: #A00; }
      ins code, del code { color: inherit; }

      span.new { color: #080; font-weight: bold; }
      span.old { color: #800; font-weight: bold; }

      #toggleparams:checked ~ * .params { display: none; }
    </style>
  </head>
  <body>
    <div class="docinfo">
      <p>Date: 2018-06-25</p>
      <address>Thomas K&ouml;ppe &lt;<a href="mailto:tkoeppe@google.com">tkoeppe@google.com</a>&gt;</address>
      <p class="skip">ISO/IEC JTC1 SC22 WG21 P1142R0</p>
      <p>To: EWG (informative only)</p>
    </div>

    <h1>Thoughts on a conservative terse syntax for constraints</h1>
  
    <p><em>Compatible. Principled. Useful.</em></p>

    <hr>
    <p style="font-family: 'DejaVu Sans', sans-serif; color: #800;">
      This document is <em>not</em> a proposal for a change. It merely serves as
      a record of ideas that the author has considered while many of us were
      developing a terse concepts syntax ahead of the San Diego meeting. The
      author endorses the resulting joint proposal,
      <em>Yet another approach for constrained declarations</em> (YAACD),
      which should be our first and foremost
      concentrated effort. Where YAACD strives for a minimal extension that
      avoids invention as much as possible, this paper describes a larger design
      that retains more of the features from the Concepts TS. If we come to
      revisit that design space in the future, it might be nice to be able to refer
      to these past thoughts.</p>
    <hr>

    <!-- fgrep -e "<h2 id=" terse.html | sed -e 's/.*id="\(.*\)">\(.*\)<\/h2>/<li><a href="#\1">\2<\/a><\/li>/g' -->
    <h2>Contents</h2>
    <ol>
      <li><a href="#summary">Summary</a></li>
      <li><a href="#demo">In a nutshell, by example</a></li>
      <li><a href="#scope">Scope, features, limitations</a></li>
      <li><a href="#def">Definitions</a></li>
      <li><a href="#terse">Terse syntaxes</a></li>
      <li><a href="#rationale">Rationale</a></li>
      <li><a href="#alternatives">Alternatives</a></li>
      <li><a href="#final">Final words</a></li>
      <li><a href="#references">References</a></li>
    </ol>

    <!-- h2 id="history">Revision history</h2>
    <ul>
      <li>P1142R0: A write-up of thoughts and discussion.</li>
    </ul -->

    <h2 id="summary">Summary</h2>

    <p>This paper describes a compromise design for a terse concept syntax,
      informed by the discussions around several past attempts. As a result,
      this design&hellip;</p>
    <ul>
      <li>is <em>useful</em> for many, but not all, common uses of concepts;</li>
      <li>is <em>regular and predictable</em>, largely free of ambiguities
        (completely so if a certain style is followed); and</li>
      <li><em>compatible</em>, with only a minor breaking change with respect to the
        post-Toronto working paper and only in uncommon cases.</li>
    </ul>
    <p>The central design decisions are a response to previous discussions, notably
      those following
      <a href="http://open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0745r1.pdf">P0745R1</a>
      and <a href="http://open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1079r0.pdf">P1079R0</a>.
      They are:</p>
    <ul>
      <li><strong>No introducer syntax.</strong> Introducers are an intriguing method to write
        certain constraint relationships among types, but the committee has consistently been
        ambivalent about any particular proposed syntax. Each suggestion allows for appealing
        code in certain situations, but suffers either from ambiguities or from a proliferation
        of novel punctuators in other situations. This proposal simply does not provide
        introducers; complex constraints need to be written with
        a <em>requires-clause</em>.</li>

      <li><strong>Terse function parameters use <code>auto</code>.</strong> After several rounds
        of discussion around terse function template syntax, the committee seems to have settled
        on wanting a syntactic marker to distinguish templates from non-templates, and moreover
        to tell which function parameters are templated. The solution to consistently mark each
        templated parameter with <code>auto</code> appears to be acceptable to most people:
        <code>void sort(Sortable auto&amp; container);</code></li>

      <li><strong>Conservative common case.</strong> For unary type concepts, the syntax from
        the working paper is retained: <code>template&lt;Sortable T&gt;</code></li>

      <li><strong>Unambiguous general case.</strong> When <code>C</code> is a concept, we never
        want <code>template&lt;C T&gt;</code> to make <code>T</code> anything other than a type.
        To effect this, unary concepts of non-type or template kind require a mandatory noun (in
        the terminology of P0807). The noun is <code>template</code> for template concepts and
        either <code>auto</code> or a type name for non-type concepts.  Optionally, type
        concepts may (but do not have to) use the noun <code>typename</code> or
        <code>class</code>; doing so makes the syntax entirely unambiguous (even when <code>C</code>
        above is not a concept).</li>
    </ul>

    <p><strong>Breaking changes:</strong> Only the last point constitutes a
      breaking change with respect to the post-Toronto working paper and the
      Concepts TS. In those, <code>C</code> is allowed to be a non-type or
      template concept, making <code>T</code> respectively a non-type or
      template <em>template-parameter</em>. In the design of this paper, a
      mandatory noun must be inserted. Non-type and template
      <em>template-parameters</em> are comparatively rare, so we consider this
      breakage to be minor.</p>

    <h2 id="demo">In a nutshell, by example</h2>

    <table class="x">
      <thead>
        <tr><th>Short syntax</th><th>Equivalent <em>requires-clause</em></th></tr>
      </thead>
      <tbody>
        <tr>
          <td class="code">auto gcd(<span class="new">Integer auto</span> a, <span class="new">Integer auto</span> b);</td>
          <td class="code"><span class="old">template&lt;typename T1, typename T2&gt; requires Integer&lt;T1&gt; &amp;&amp; Integer&lt;T2&gt;</span><br>auto gcd(<span class="old">T1</span> a, <span class="old">T2</span> b);</td>
        </tr>

        <tr>
          <td class="code">template&lt;<span class="new">Iterator</span> I&gt;<br>void sort(I first, I last);</td>
          <td class="code">template&lt;<span class="old">typename</span> I&gt; <span class="old">requires Iterator&lt;I&gt;</span><br>void sort(I first, I last);</td>
        </tr>

        <tr>
          <td class="code">template&lt;<span class="new">Even</span> int N&gt;<br>void AddConsecutivePairs(const <span class="new">auto</span> (&amp;arr)[N], <span class="new">OutputIterator auto</span> it);</td>
          <td class="code">template&lt;int N, <span class="old">typename T, typename Out</span>&gt; <span class="old">requires Even&lt;N&gt; &amp;&amp; OutputIterator&lt;Out&gt;</span><br>void AddConsecutivePairs(const <span class="old">T</span> (&amp;arr)[N], <span class="old">Out</span> it);</td>
        </tr>

        <tr>
          <td class="code">template&lt;<span class="new">ContainerLike</span> template Tmpl, typename T&gt;<br>struct MyAdaptor;</td>
          <td class="code">template&lt;<span class="old">template&lt;typename&gt; typename Tmpl</span>, typename T&gt; <span class="old">requires ContainerLike&lt;Tmpl&gt;</span><br>struct MyAdaptor;</td>
        </tr>

        <tr><th class="sep" colspan="2">Constrained variable and return type declarations</th></tr>
        <tr><td colspan="2">[<em>Note</em>: Only type concepts can be applied to constrained declarations
            (variables, function return types, function parameters). &ndash;&nbsp;<em>end note</em>]</td></tr>
        <tr>
          <td class="code"><span class="new">Sortable</span> auto x = f();</td>
          <td>Type of <code>x</code> is deduced, ill-formed if it does not satisfy the type concept.<br>
            Roughly equivalent to <code>static_assert(Sortable&lt;decltype(x)&gt;)</code>.</td>
        </tr>

        <tr>
          <td class="code"><span class="new">Sortable</span> auto f() { /* ... */ }</td>
          <td>Return type of <code>f</code> is deduced from the body, ill-formed if it does not satisfy the type concept.</td>
        </tr>
     </tbody>
    </table>

    <h2 id="scope">Scope, features, limitations</h2>

    <p>This paper describes a set of &ldquo;terse concept syntaxes&rdquo;
      based on the <em>adjective syntax</em> that has been developed in
      <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0807r0.html">P0807</a>.
      It consists of the core proposal of P0807, plus the future extensions for constrained
      function parameters, function return types and variable declarations discussed therein,
      and the modification to make <code>typename</code>/<code>class</code> optional.
    </p>

    <p>They key design points of the resulting proposal are:</p>

    <ul>
      <li>Unary concepts only (defined below). Retains the <em>partial-concept-id</em>s from
        the working paper (no breaking change there).</li>
      <li>No ambiguities: the &ldquo;kind&rdquo; of a template parameter is always syntactically
        marked (via type name, <code>auto</code>, <code>typename</code>/<code>class</code> or
        <code>template</code>) (unless <code>typename</code>/<code>class</code> is made optional).</li>
      <li>Unused names can be omitted with no change in meaning (just like everywhere else).</li>
      <li>Names are introduced by the familiar template head. No new introducer syntax.</li>
      <li>Binary concepts require the full <em>requires-clause</em> syntax:
        <code>template&lt;typename I, typename S&gt; requires Iterator&lt;I, S&gt;</code>.</li>
    </ul>

    <p>The upside of the proposed approach is that it is a minimal extension that retains many
      of the familiar properties of template syntax. The major downside is that it can only handle
      unary concepts. There is no short syntax for complex concepts whatsoever. Whether this is a
      problem depends on how important we consider unary concepts relative to all uses of concepts,
      i.e. whether we can help a large fraction of the use cases.</p>

    <p>Details appertaining to <em>partial-concept-id</em>s are not spelled out in this paper.
      P0807 contains full examples.</p>

    <p>Details around constraining <code>auto</code> declarations that are part of structured
      bindings, trailing return types or <code>decltype(auto)</code> are not discussed here,
      but should follow the conclusions of YAACD.</p>

    <h2 id="def">Definitions</h2>

    <p>A concept is a <em>unary concept in the context where it is used</em> if its prototype parameter
      (or parameter pack) is the only free parameter (or pack), and all further parameters (if any)
      have been bound to a fixed set of arguments (via a <em>partial-concept-id</em>). The kind of the
      prototype parameter (pack) determines the kind of the unary concept (names following P0807):</p>

    <table class="x">
      <caption>Four kinds of concept</caption>
      <thead>
        <tr><th>Kind of concept</th><th>Example</th></tr>
      </thead>
      <tbody>
        <tr><th>Type concept</th><td><code>template&lt;<strong>typename</strong> T&gt;<br>concept Integral = /* ... */;</code></td></tr>
        <tr><th>Value concept</th><td><code>template&lt;<strong>int</strong> N&gt;<br>concept Even = N % 2 == 0;</code></td></tr>
        <tr><th>Auto concept</th><td><code>template&lt;<strong>auto</strong> N&gt;<br>concept EvenIntegral = Integral&lt;decltype(N)&gt; &amp;&amp; N % 2 == 0;</code></td></tr>
        <tr><th>Template concept</th><td><code>template&lt;<strong>template</strong>&lt;typename&gt; typename Tmpl&gt;<br>concept Foo = /* ... */;</code></td></tr>
      </tbody>
    </table>

    <p>Concepts that are not unary in a particular context are called <em>complex concepts</em>.
      A constraint that uses a complex concept or that uses multiple concepts (or both) is
      called a <em>complex constraint</em>. [<em>Example</em>: <code>template&lt;typename X, typename Y, typename Z&gt;
      requires C1&lt;X, Y&gt; &amp;&amp; C2&lt;Z&gt; &amp;&amp; C3&lt;Z&gt;</code> is a
      complex constraint. &mdash;&nbsp;<em>end example</em>]</p>

    <h2 id="terse">Terse syntaxes</h2>

    <h3>Constrained parameters</h3>

    <p>This is the content of P0807. Adjective syntax can be applied to a
      <em>constrained-parameter</em> of all kinds. Note in particular
      that value, type and auto concepts can be applied to value, type
      and auto parameters in various combinations.</p>
    <div class="code" style="white-space: pre"><!--
      -->template &lt;Integral     typename T&gt; struct X;   // type concept, type parameter
template &lt;Integral        class T&gt; struct X;   // synonym for the above
template &lt;Integral              T&gt; struct X;   // same as above, "typename"/"class" is optional
template &lt;Even         int      N&gt; struct X;   // value concept, value parameter
template &lt;EvenIntegral auto     N&gt; struct X;   // auto concept, auto parameter
template &lt;EvenIntegral int      N&gt; struct X;   // auto concept, value parameter, deduce "auto" as "int"
template &lt;Even         auto     N&gt; struct X;   // value concept, auto parameter, ill-formed unless decltype(N) == int
template &lt;Integral     auto     N&gt; struct X;   // type concept, auto parameters, means "requires Integral&lt;decltype(N)&gt;"<!--
    --></div>

    <p>Note that constraints can be relaxed by dropping the concept name (except
      that an omitted &ldquo;<code>typename</code>&rdquo; needs to be reintroduced).</p>

    <p>Complex concepts cannot be used with this syntax and instead need the full <em>requires-clause</em> syntax.</p>

    <h3>Terse function templates</h3>

    <p>The goal of a terse function template syntax is to use the function parameters to
      turn the function into a function template. The only part of the template that can
      be parameterised in this context is the function parameter <em>type</em>, and thus
      we only need to consider type concepts here.</p>
    <p>The proposed terse syntax does not allow the deduced types to be named. If a named
      template parameter is required, the traditional template head should be used instead.</p>

    <div class="code" style="white-space: pre"><!--
      -->void f(MyInt n);                                   // not a template because no "auto" or "template"
void f(Integral auto n);                           // template because "auto"
void f(Integral auto n1, Integral auto n2);        // two separately deduced types
template&lt;Integral typename T&gt; void f(T n1, T n2);  // one type
void f(MyInt n1, Integral auto n2);                // function template, n1 is converted, n2 is deduced
void f(auto n1, MyInt n2, auto n3);                // function template, two unconstrained template parameters

template&lt;typename I, typename S&gt; requires Iterator&lt;I, S&gt;
  void f(I iter, S sentinel);  // template constrained by complex concept (no short syntax available)

// mix and match (constrained parameters and terse function syntax)
dispatch_sort(v, []&lt;Integral typename T&gt;(T begin, T end, const Predicate auto&amp; p) { /* ... */ });
</div>

    <h3>Constrained declarations</h3>

    <p>This part of the syntax allows constrained forms of deduced types
      for variable and function return type declarations.</p>

    <div class="code" style="white-space: pre"><!--
      -->Integral auto f(int n, double x) { return x * n; }

auto x = f();           // unconstrained
Integral auto y = g();  // constrained type</div>

    <h2 id="rationale">Rationale</h2>

    <p>How did we get here? The short syntax forms that appear in the
      Concepts TS are appealingly short, but suffer from ambiguities: constrained
      template parameters do not syntactically show whether they are of type,
      non-type or template kind, and function parameters do not syntactically
      signal whether the function is in fact a function template. When the bulk
      of the concepts extensions were adopted in the IS working paper in Toronto 2017,
      the short syntax forms other than constrained parameters were excluded, as the
      committee did not feel confident about the syntax.</p>

    <p>A number of proposals for short syntax forms have been made since Toronto. Many
      of them involved new syntax to <em>introduce</em> names for the types that were
      being constrained, and in the process addressing the aforementioned ambiguities
      to varying degrees. However, two main questions kept coming back:</p>
    <ul>
      <li>How can function templates be distinguished from non-templates?</li>
      <li>How verbose is the common case?</li>
    </ul>
    <p>None of the proposals were entirely satisfying:</p>
    <ul>
      <li><code>void sort(Shape s, Number n);</code> &mdash; the syntax from the Concepts TS,
        which makes <em>no</em> distinction between templates and non-templates (but in
        which the common case is pleasantly short).</li>
      <li><code>template void sort(Shape s, Number n);</code> &mdash; from P1079, this syntax
        is still short, but it leaves functions with more than one parameter ambiguous as to
        <em>which</em> parameters are templated.</li>
      <li><code>void sort(Shape s, Number{T} n);</code> &mdash; from P0745, this syntax
        is unambiguous and fairly short, and offers additional expressiveness by allowing the
        deduced type to be named. However, the syntax is somewhat novel, and the full
        proposal puts empty braces (<code>{}</code>) in several common places. The proposal
        also contains situations where leaving out a parameter name changes the meaning of a
        declaration.</li>
    </ul>
    <p>At the same time, proposals had been made for an &ldquo;adjective&rdquo; syntax,
      which adds the concept name as an additional, whitespace separated token in the
      declaration. This syntax is often more verbose, but it can be made very regular
      without ambiguities. While previous proposals did not gain traction, one particular
      adjective-based approach is being considered now to resolve the above problems:</p>
    <ul>
      <li><code>void sort(Shape s, Number auto n);</code> &mdash; adjective syntax, the
        noun <code>auto</code> signals that this is a template.</li>
    </ul>
    <p>Once we allow a value to have its (deduced) type constrained by a concept
      adjective, it is only a short jump to allow the same for deduced non-type
      template parameters: <code>template&lt;Number auto N&gt;</code>. But in this
      position, <code>Number</code> need not just be a type-concept, but it can also
      reasonably be an <code>auto</code>-concept (i.e. a concept that constrains the
      value): <code>template&lt;EvenIntegral auto N&gt;</code>.
      Once we have value-concept adjectives, we should allow them not just
      on <code>auto</code>, but on concrete types, too: <code>template&lt;Even int
      N&gt;</code>.</p>
    <p>This gives us an inroad for an adjective syntax for constrained parameters.
      When non-type parameters take their constraint as adjectives, we are closer
      to avoiding ambiguities. We can take this further and move the constraints
      into adjective position for the other parameter kinds. For template
      <em>template-parameters</em>, this means saying <code>template&lt;ContainerLike
      template Tmpl&gt;</code>. (We can even allow the full template head here to
      make <code>Tmpl</code> a specific template.) This only leaves type parameters.
      For consistency, they should also support an adjective style: <code>template&lt;Iterator
      class T&gt;</code> (or <code>typename</code>). However, since this kind of parameter
      is by far the most widely used kind, the <code>typename</code>/<code>class</code>
      noun is optional.</p>
    <p>When all is said and done, we end up with mandatory adjective syntax for
      constrained function parameters, function return types and variable declarations
      as a new feature (where the noun is always <code>auto</code> and the constraint
      is always via type-concept). We also make the noun mandatory for non-type and template
      <em>template-parameters</em>, which is a breaking change, but those kinds of parameters
      are relatively rare. The result is a coherent syntax that works predictably and is
      largely free from ambiguities. And it can be made fully unambiguous if the
      <code>typename</code>/<code>class</code> noun is used.</p>

    <h2 id="alternatives">Alternatives</h2>

    <ul>
      <li>Compared to the post-Toronto working paper, we could only add terse declaration syntax
        (constrained <code>auto</code>),
        and not change anything else (i.e. leave constrained parameters as they were).
        This means that we have a function parameter or variable declaration <code>Number auto n</code> of
        constrained type, but we would not be able to constrain a non-type template parameter.
        The syntax <code>template&lt;Number N&gt;</code> makes <code>N</code> a type, not a
        value, and a value <code>template&lt;auto N&gt;</code> could only be constrained by
        a <em>requires-clause</em>. By contrast with the design here presented,
        this alernative approach does not allow simply dropping the concept to get to a less
        constrained (but still valid) parameter.</li>
      <li>Add the terse function syntax, and disallow constrained parameters for
        anything other than type-concepts. This avoids ambiguities among the
        constrained parameters, but it feels needlessly restrictive. It is a
        breaking change from the working paper without a good replacement.
        If <code>template&lt;auto N&gt;</code> is valid, and declarations of the
        form <code>auto N</code> can be constrained elsewhere, it would be
        surprising that the template parameter cannot be constrained in the same
        way.</li>
    </ul>

    <p><strong>Comparison with YAACD:</strong> The main difference between the present
      design and that proposed for San Diego is that the present design allows
      the use of constrained parameters for template template-parameters (using the
      mandatory noun <code>template</code>), it allows non-type parameters to
      have non-type constraints (auto- and value-concepts), and it allows optionally
      to specify the <code>typename</code>/<code>class</code> noun.</p>

    <h2 id="final">Final words</h2>

    <ul>
      <li>Terse syntax is situational. If you need to introduce a deduced name
        (e.g. to use the name repeatedly), the traditional template head syntax
        is preferable. Constrained declarations have no introducer alternative;
        <code>decltype</code> must be used to obtain the deduced type.</li>
      <li>&ldquo;Saying <code>Integral typename T</code> is verbose.&rdquo;
        Yes, it is, but keeping the noun mandatory makes the syntax regular
        and predictable. When <code>typename</code>/<code>class</code> is omitted,
        we cannot tell whether <code>template&lt;A B&gt;</code> is
        a type parameter constrained by concept <code>A</code> or a non-type parameter
        of type <code>A</code>. (But we can live with this in many cases.)
        This proposal leaves the choice to the user.</li>
      <li>Is this approach viable? It depends on how much more common unary
        concepts are than complex concepts. Complex concepts may benefit from
        an introducer syntax, which this approach will not achieve. If complex
        concepts are comparatively rare, then the adjective approach helps a
        large number of use cases without introducing very novel syntax.</li>
    </ul>

    <h2 id="references">References</h2>
    <ul>
      <li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4549.pdf">N4549</a>:
        Andrew Sutton, <em>Programming Languages &mdash; C++ Extensions for Concepts</em>, 2015-07-27</li>
      <li><a href="http://open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0745r1.pdf">P0745R1</a>:
        Herb Sutter, <em>Concepts in-place syntax</em>. 2018-04-29</li>
      <li><a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0807r0.html">P0807R0</a>:
        Thomas K&ouml;ppe, <em>An Adjective Syntax for Concepts</em>, 2017-10-12</li>
      <li><a href="http://open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1079r0.pdf">P1079R0</a>:
        Bjarne Stroustrup, <em>A minimal solution to the concepts syntax problem</em>, 2018-05-06</li>
      <li>[YAACD]: Ville Voutilainen et al., <em>Yet another approach for constrained declarations</em>, 2018-06-25</li>
    </ul>
  </body>
</html>
