Topic: Making C++ more functional
Author: Xeo <hivemaster@hotmail.de>
Date: Tue, 23 Apr 2013 01:03:19 -0700 (PDT)
Raw View
------=_Part_55_22945530.1366704199005
Content-Type: multipart/alternative;
boundary="----=_Part_56_4442741.1366704199005"
------=_Part_56_4442741.1366704199005
Content-Type: text/plain; charset=ISO-8859-1
(Formerly called "Lifting overload sets into function objects", but that
describes just one of the things it does, not what it brings, so I changed
the name.)
Linked below is an updated version of N3617, a proposal to open C++ up to
more functional programming. The idea is to add a language feature that
allows what functional languages naturally have - passing functions by
their name.
Discussions and ideas are more than welcome.
(Note: I deleted the prior post because I made a mistake and left the Nnnnn
in the updated document.)
<https://dl.dropboxusercontent.com/u/14327987/functional_cpp.html>
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
------=_Part_56_4442741.1366704199005
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
(Formerly called "Lifting overload sets into function objects", but that=20
describes just one of the things it does, not what it brings, so I=20
changed the name.)<br>Linked below is an updated version of N3617, a propos=
al to open C++ up to more functional=20
programming. The idea is to add a language feature that allows what functio=
nal=20
languages naturally have - passing functions by their name.<br><br>Discussi=
ons and ideas are more than welcome.<br><br>(Note: I deleted the prior post=
because I made a mistake and left the Nnnnn in the updated document.)<br><=
a href=3D"https://dl.dropboxusercontent.com/u/14327987/functional_cpp.html"=
></a>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
<br />
<br />
------=_Part_56_4442741.1366704199005--
------=_Part_55_22945530.1366704199005
Content-Type: text/html; charset=US-ASCII; name=functional_cpp.html
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment; filename=functional_cpp.html
X-Attachment-Id: 70a3efca-e902-41af-be6c-f237b3196a49
Content-ID: <70a3efca-e902-41af-be6c-f237b3196a49>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"><head>
<title>functional_cpp</title>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">
<style type="text/css">
/* GitHub stylesheet for MarkdownPad (http://markdownpad.com) */
/* Author: Nicolas Hery - http://nicolashery.com */
/* Version: 29d1c5bc36da364ad5aa86946d420b7bbc54a253 */
/* Source: https://github.com/nicolahery/markdownpad-github */
/* RESET
=============================================================================*/
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
}
/* BODY
=============================================================================*/
body {
font-family: Helvetica, arial, freesans, clean, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
background-color: #fff;
padding: 20px;
max-width: 960px;
margin: 0 auto;
}
body>*:first-child {
margin-top: 0 !important;
}
body>*:last-child {
margin-bottom: 0 !important;
}
/* BLOCKS
=============================================================================*/
p, blockquote, ul, ol, dl, table, pre {
margin: 15px 0;
}
/* HEADERS
=============================================================================*/
h1, h2, h3, h4, h5, h6 {
margin: 20px 0 10px;
padding: 0;
font-weight: bold;
-webkit-font-smoothing: antialiased;
}
h1 tt, h1 code, h2 tt, h2 code, h3 tt, h3 code, h4 tt, h4 code, h5 tt, h5 code, h6 tt, h6 code {
font-size: inherit;
}
h1 {
font-size: 28px;
color: #000;
}
h2 {
font-size: 24px;
border-bottom: 1px solid #ccc;
color: #000;
}
h3 {
font-size: 18px;
}
h4 {
font-size: 16px;
}
h5 {
font-size: 14px;
}
h6 {
color: #777;
font-size: 14px;
}
body>h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}
/* LINKS
=============================================================================*/
a {
color: #4183C4;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* LISTS
=============================================================================*/
ul, ol {
padding-left: 30px;
}
ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}
ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}
dl {
padding: 0;
}
dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}
dl dt:first-child {
padding: 0;
}
dl dt>:first-child {
margin-top: 0px;
}
dl dt>:last-child {
margin-bottom: 0px;
}
dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
dl dd>:first-child {
margin-top: 0px;
}
dl dd>:last-child {
margin-bottom: 0px;
}
/* CODE
=============================================================================*/
pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}
pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}
pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}
pre code, pre tt {
background-color: transparent;
border: none;
}
/* QUOTES
=============================================================================*/
blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}
blockquote>:first-child {
margin-top: 0px;
}
blockquote>:last-child {
margin-bottom: 0px;
}
/* HORIZONTAL RULES
=============================================================================*/
hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}
/* TABLES
=============================================================================*/
table th {
font-weight: bold;
}
table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}
table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}
table tr:nth-child(2n) {
background-color: #f8f8f8;
}
/* IMAGES
=============================================================================*/
img {
max-width: 100%
}
</style>
</head>
<body>
<p>Document Number: Dnnnn</p>
<p>Date: 2013-03-14</p>
<p>Project: Programming Language C++, Evolution Working Group</p>
<p>Reply-to: Philipp Juschka <p.juschka at hotmail dot de></p>
<h1>Making C++ more functional</h1>
<h2>Introduction</h2>
<p>Many generic libraries, including the standard library, draw from the
functional programming paradigm, where passing around functions is a
common occurrence. This proposal aims to allow the usage of functional
techniques with plain old overloaded functions and function templates
without needing to create a function object by hand.</p>
<p><strong>In a nutshell, this proposal aims to provide a way to effectively pass functions <em>by their name</em>.</strong></p>
<h2>Motivation and Scope</h2>
<p>When dealing with generic algorithms, like the function templates in <code><algorithm></code>, it can be quite cumbersome to pass an overloaded function or a function template, because the user needs to either</p>
<ul>
<li>specify the exact type of the resulting function pointer through a cast or binding it to an appropriate variable first; or</li>
<li>wrap the function in a lambda, still having to specify the argument types [<em>Note:</em> The polymorphic lambda proposal will lessen the burden here, but this proposal aims to go farther than regular syntactic sugar]</li>
</ul>
<p>Example:</p>
<pre><code>// a
std::vector<int> vi{ /* ... */ };
std::vector<std::string> vs{ /* ... */ };
using tuple_t = std::tuple<int, std::string>;
std::vector<tuple_t> vt;
std::transform(vi.begin(), vi.end(), vs.begin(), std::back_inserter(vt),
&std::make_tuple<int&, std::string&>);
// b (assumes a)
std::vector<float> vf{ /* ... */ };
std::vector<std::tuple<int, std::string, float>> vt2;
using namespace std::placeholders;
// transforms the elements of vf to a single-element tuple each,
// and passes that plus the tuple from vt to std::tuple_cat
std::transform(vt.begin(), vt.end(), vf.begin(), std::back_inserter(vt2),
std::bind(&std::tuple_cat<tuple_t&, std::tuple<float>>, _1,
std::bind(&std::make_tuple<float&>)));
</code></pre>
<p>As can be seen, the template arguments need to be explicitly provided
everywhere, and they need to be references at times because the
template is declared as taking <code>(T&&...)</code>. As such,
perfect forwarding and reference collapsing need to be kept in mind when
passing the template arguments, which distracts from the actual goal.</p>
<p>This doesn't lend itself to clean or highly generic code, especially
when aiming to write in a functional style. It also hinders the ability
to provide useful higher-order functions and abstractions, like monadic
binding, as the syntactic contortions needed to make them work would
diminish the value they would bring otherwise.</p>
<h2>Impact on the Standard</h2>
<p>No impact on the current standard, no other dependencies. This paper
only references the polymorphic lambda proposal[1] and the polymorphic
operator functors proposal[2] for explanations.</p>
<p>For [1], there may be a clash with the expression-only syntax in combination with the omitted parameter-list and captures: <code>[]foo()</code> would be a <em>lambda-expression</em> that invokes <code>foo</code> in [1], and a <em>lifting-expression</em> which is immediately invoked (and as such equivalent to just <code>foo()</code>) in this proposal.</p>
<h2>Impact on existing code</h2>
<p>The proposed <code>[]id-expression</code> and <code>[](<operator>)</code> syntax are currently ill-formed, so no impact on existing code.</p>
<h2>Discussion</h2>
<p>The envisioned syntax would be a simple <code>[]id-expression</code>, which I call a <em>lifting-expression</em> or <em>lifting-lambda</em>
(which is of course open to bikeshedding). The idea started out as just
having a short-hand notation for the following polymorphic lambda, if
that proposal was to be adopted:</p>
<pre><code>[](auto&&... vs){ return id-expression(std::forward<decltype(vs)>(vs)...); }
</code></pre>
<p>The above allows the user to pass any overloaded function or function
template as if it were a function object, bypassing the main issues
mentioned in the motivation. However, there are drawbacks:</p>
<ul>
<li>Writing out the whole lambda expression is tedious and introduces unnecessary clutter.</li>
<li>It only allows exactly that invocation form and dismisses member functions and member data.</li>
<li>Operator support is incomplete, only allowing the non-member direct invocation syntax <code>operator@(arguments...)</code>, ignoring member operator overloads as well as the special rules for operator function name look-up.</li>
</ul>
<p>These drawbacks, especially the third one, can not be solved in a
"library"-way (say, with a macro). The solution needs to be at the
language level since it partly depends on what grammar-elements are used
as the <em>id-expression</em>. There is, however, a close approximation in the form of a library-only solution[4] from the author of [3].</p>
<p>The question then becomes what the <code>[]id-expression</code> should represent, and how the resulting object should behave. The semantics of <code>INVOKE</code> seemed like a good starting point, but since <code>INVOKE</code> only deals with objects and not with <em>id-expressions</em> themselves, it can only serve as a rough basis for behaviour. Special handling only needs to be done for <em>operator-function-id</em>s that are <em>unqualified-id</em>s, as far as I can see. With that, a <em>lifting-expression</em> can be described in terms of <code>LIFTED_INVOKE</code>, similar to how <code>std::bind</code> and others are described in terms of <code>INVOKE</code>.</p>
<h2>Design Decisions</h2>
<p>The main goal is to provide a concise and unobtrusive syntax, while
yielding an intuitive result. For that purpose, the semantics borrow
from <code>INVOKE</code>'s cases, while the grammar borrows from the existing <em>lambda-expression</em> syntax. The <code>[]id-expression</code>
syntax is open to bike-shedding discussions, of course, but I chose it
as a first option because it seems rather natural, and others[3] also
seem to have reached this conclusion independently.</p>
<p>The <code>[]operator-function-id</code> special case may be seen as a
generalization to 'Making Operator Functors greater<>'[2],
although I personally would use those for clarity. It may be worth
considering which operators would actually be allowed by this form, as <code>[]operator++</code> and <code>[]operator--</code>
are inherently ambiguous. These denote both prefix and postfix
versions, and would need a way to disambiguate. One idea is that <code>[]operator++(int)</code> and <code>[]operator--(int)</code> as a special syntax would denote the postfix versions, but this will have to be investigated further.</p>
<p>Another thing that must be considered is mirroring the data-member cases of <code>INVOKE</code>. These can be ambiguous with free function overloads / templates, as in the following example: </p>
<pre><code>template<class T>
void foo(T){}
template<class T>
void bar(T){}
struct X{
int foo;
void bar(){}
};
void f(){
X x;
[]foo(x); // would return X::foo with the current specification
// maybe not intended
[]bar(x); // would invoke X::bar, also maybe not intended
}
</code></pre>
<p>One idea that came up in a discussion was to allow <code>[].id-expression</code> to denote members-only (a bit like <code>std::mem_fn</code>) and remove the member cases except when the <em>lifting-expression</em> is in the literal context of a class (e.g., a member function). This, however, will need to be further investigated.</p>
<h2>Technical Specification</h2>
<p>[<em>Note:</em> Raw and annotated.]</p>
<p>Define <code>LIFTED_INVOKE(id-expression, a1, a2, ..., aN)</code> as follows:</p>
<ul>
<li><code>LIFTED_INVOKE_OP(id-expression, a1, a2, ..., aN)</code> when the <em>id-expression</em> is an <em>unqualified-id</em> and represents an <em>operator-function-id</em>;</li>
<li><code>a1.id-expression(a2, ..., aN)</code> when the expression would represent a member function invocation;</li>
<li><code>(*a1).id-expression(a2, ..., aN)</code> when the previous item does not apply and the expression would represent a member function invocation;</li>
<li><code>a1.id-expression</code> when <code>N == 1</code> and the <em>id-expression</em> names a data-member of <code>a1</code>;</li>
<li><code>(*a1).id-expression</code> when <code>N == 1</code>, the <em>id-expression</em> names a data-member of the result of <code>(*a1)</code> and the previous item does not apply;</li>
<li><code>id-expression(a1, a2, ..., aN)</code> in all other cases.</li>
</ul>
<p>Define <code>LIFTED_INVOKE_OP(operator-function-id, a1, a2, ..., aN)</code> as follows, where @ represents the respective operator:</p>
<ul>
<li><code>@a1</code> when <code>N == 1</code>;</li>
<li><code>a1 @ a2</code> when <code>N == 2</code>;</li>
<li><code>a1[a2]</code> when <code>N == 2</code> and the <em>operator-function-id</em> is <code>operator[]</code>;</li>
<li><code>a1(a2, ..., aN)</code> when <code>N >= 1</code> and the <em>operator-function-id</em> is <code>operator()</code>;</li>
<li><code>(a1->*a2)(a3, ..., aN)</code> when <code>N >= 2</code>, the <em>operator-function-id</em> is <code>operator->*</code> and <code>a2</code> is a pointer-to-member-function;</li>
<li><code>a1->*a2</code> when <code>N == 2</code>, the <em>operator-function-id</em> is <code>operator->*</code> and <code>a2</code> is a pointer-to-member-data;</li>
<li><code>@(a1)</code> when <code>N == 1</code> and the operator is one of <code>new</code>, <code>new[]</code>, <code>delete</code> or <code>delete[]</code></li>
</ul>
<p>[<em>Note:</em> The split between <code>LIFTED_INVOKE</code> and <code>LIFTED_INVOKE_OP</code> is deliberate and intended for clarity.]</p>
<p>Extend the grammar rule for <em>primary-expression</em>:</p>
<pre><code>primary-expression:
[...]
lifting-expression
</code></pre>
<p>Add a new grammar rule:</p>
<pre><code>lifting-expression:
[] id-expression
[] (<operator>)
</code></pre>
<p>(Where <code><operator></code> is one of the operators from the table in [over.oper]/1 in N3290.)</p>
<p>The second grammar rule is equivalent to <code>[] operator <operator></code>. [<em>Example:</em> <code>[](+)</code> is the same as <code>[]operator+</code>.]</p>
<p>A <em>lifting-expression</em> would then evaluate to a <em>lifting-lambda</em> (TBD) <code>h</code> and the effects of <code>h(a1, a2, ..., aN)</code> shall be <code>LIFTED_INVOKE(id-expression, a1, a2, ..., aN)</code> (also called the <em>lifted expression</em>) if the resulting expression is well-formed. Otherwise, the <code>operator()</code> of the <em>lifting-lambda</em> shall not participate in overload resolution. [<em>Note:</em> This makes the <em>lifting-lambda</em> usable in code that employs expression SFINAE.]</p>
<p>The <code>operator()</code> shall be perfect-returning. This means that the expression <code>h(a1, a2, ..., aN)</code>
has the same value-category as the lifted expression - prvalues are
returned as prvalues, xvalues as xvalues, and lvalues as lvalues. [<em>Note:</em> A simple <code>auto&& operator()(...)</code>
(assuming deduced return types were in the language) would not suffice,
as that would return dangling rvalue references if the final expression
evaluates to a prvalue.]</p>
<p>Furthermore, the <code>operator()</code> shall be declared <code>constexpr</code> if the lifted expression results in a constant expression. It shall also be declared <code>noexcept(true)</code> if the lifted expression is <code>noexcept(true)</code>.</p>
<p>[<em>Note:</em> The intent is that the <em>lifting-lambda</em> acts
as much as the lifted expression would. This means that it
perfect-forwards all arguments and evaluates to the same value-category.
It should also be usable where a constant expression is expected if the
lifted expression would be, and have the same <code>noexcept</code>ness. The perfect-returning semantics are basically mirroring the <code>decltype</code> semantics.]</p>
<p>Reworked and new examples:</p>
<pre><code>// a
std::vector<int> vi{ /* ... */ };
std::vector<std::string> vs{ /* ... */ };
std::vector<std::tuple<int, std::string>> vt;
std::transform(vi.begin(), vi.end(), vs.begin(), std::back_inserter(vt),
[]std::make_tuple);
// b (assumes a)
std::vector<float> vf{ /* ... */ };
std::vector<std::tuple<int, std::string, float>> vt2;
using std::placeholders::_1;
// transforms the elements of vf to a single-element tuple,
// and passes that plus the elements from vt to std::tuple_cat
std::transform(vt.begin(), vt.end(), vf.begin(), std::back_inserter(vt2),
std::bind([]std::tuple_cat, _1, std::bind([]std::make_tuple)));
// *********************
// c
struct user{
int id;
std::string name;
};
std::vector<user> users{ {4, "Bjarne"}, {1, "Herb"}, {3, "Scott"},
{0, "Stephan"}, {2, "Andrei"} };
// ordered_by implementation left as an exercise to the reader
std::sort(users.begin(), users.end(), ordered_by([]id));
// [(0, "STL"), (1, "Herb"), (2, "Andrei"), (3, "Scott"), (4, "Bjarne")]
std::sort(users.begin(), users.end(), ordered_by([]name));
// [(2, "Andrei"), (4, "Bjarne"), (1, "Herb"), (3, "Scott"), (0, "Stephan")]
// *********************
// d
std::vector<int> v{ /* ... */ }, v2{ /* ... */ };
std::transform(v.begin(), v.end(), v2.begin(), black_hole_iterator{}, []operator*=);
</code></pre>
<h2>Further ideas</h2>
<p>An idea was to allow <code>[]obj.id-expression</code> for <code>this</code>-binding (like <code>std::bind</code>) - <code>[]x.foo</code> would then create a nullary <em>lifting-lamba</em>.</p>
<p>A short-hand notation was also suggested for <em>operator-function-id</em>s, to make them even more concise: <code>[](@)</code>. For those who know Haskell, the following snippet should feel very familiar: <code>foldl([](+), 0, xs)</code>, which is incidentally where this syntax idea came about from.<br>
For those not familiar with Haskell, the <code>(+)</code> syntax can be used to make an operator invokable like a function, i.e. <code>(+) 1 2</code> instead of <code>1 + 2</code>, and as such passable to higher-order functions.<br>
This would allow the last example to be written as:</p>
<pre><code>std::transform(v.begin(), v.end(), v2.begin(), black_hole_iterator{}, [](*=));
</code></pre>
<h2>Acknowledgements</h2>
<p>Many thanks to R. Martinho Fernandes, DeadMG and others from the
Stack Overflow Lounge<C++> chat room, as well as Richard Smith and
others from the #llvm channel on irc.oftc.net, for their helpful
suggestions and input on discussions.<br>
Also thanks to Giovanni Piero Deretta for comments and his library-only approximation.</p>
<h2>References</h2>
<p>[1] Proposal for Generic (Polymorphic) Lambda Expressions, F. Vali, H. Sutter, D. Abrahams<br>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf">http://open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3418.pdf</a></p>
<p>[2] Making Operator Functors greater<>, Stephan T. Lavavej<br>
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3421.htm">http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3421.htm</a></p>
<p>[3] C++Next blog comment, Giovanni Piero Deretta<br>
<a href="http://cpp-next.com/archive/2011/11/having-it-all-pythy-syntax/#q-1732">http://cpp-next.com/archive/2011/11/having-it-all-pythy-syntax/#q-1732</a><br>
And also the polymorphic lambda proposal above, albeit with different semantics.</p>
<p>[4] Quoting in C++, Giovanni Piero Deretta<br>
<a href="https://github.com/gpderetta/Experiments/blob/master/quote.hpp">https://github.com/gpderetta/Experiments/blob/master/quote.hpp</a><br>
<a href="https://github.com/gpderetta/Experiments/blob/master/tests/quote_test.cc">https://github.com/gpderetta/Experiments/blob/master/tests/quote_test.cc</a></p>
<h2>Revisions</h2>
<p>2013-03-14<br>
Initial version</p>
<p>2013-04-10</p>
<ul>
<li>Renamed to make the objective clearer</li>
<li>Added <code>[]obj.id-expression</code> idea</li>
<li>Added <code>constexpr</code> and <code>noexcept</code> considerations</li>
<li>Added <code>[](@)</code> short-hand notation idea</li>
<li>Added examples of the tediousness of passing function templates</li>
<li>Added better examples for lifting-lambdas' awesomeness</li>
<li>Added link to library-only approximation</li>
<li>Expanded discussion on ambiguity between non-members and members</li>
<li>Added <code>new</code>, <code>new[]</code>, <code>delete</code> and <code>delete[]</code> operators to <code>LIFTED_INVOKE_OP</code></li>
<li>Added considerations w.r.t. syntactical clashes with expression-only lambdas</li>
</ul>
</body></html>
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
------=_Part_55_22945530.1366704199005--
.
Author: Nikolay Ivchenkov <mk.ivchenkov@gmail.com>
Date: Wed, 24 Apr 2013 01:29:29 -0700 (PDT)
Raw View
------=_Part_2056_30499971.1366792169907
Content-Type: text/plain; charset=windows-1252
Content-Transfer-Encoding: quoted-printable
It's good to see that we have an official proposal about forwarding
lambdas. I was going to write similar paper, but now we can just
discuss this one.
> When dealing with generic algorithms, like the function templates in
> <algorithm>, it can be quite cumbersome to pass an overloaded
> function or a function template
.... or a function with default arguments:
std::wstring to_wstring(
std::string const &s,
encoding_t encoding =3D default_encoding());
std::vector<std::string> src =3D source();
std::vector<std::wstring> dst;
std::transform(
src.begin(),
src.end(),
std::back_inserter(dst),
to_wstring); // won't work
In some cases we can't safely use address of a member function (even
with further explicit conversions), because the function's type is not
fixed: according to C++11 - 17.6.5.5,
An implementation may declare additional non-virtual member
function signatures within a class:
=97 by adding arguments with default values to a member function
signature; [Footnote: Hence, the address of a member
function of a class in the C++ standard library has an
unspecified type.] [ Note: An implementation may not add
arguments with default values to virtual, global, or
non-member functions.=97end note ]
=97 by replacing a member function signature with default values
by two or more member function signatures with equivalent
behavior; and
=97 by adding a member function signature for a member function
name.
In general, we can take address of only those functions which are
guarateed to have the same type (and also be non-overloaded
non-templates if we don't use further casts) forever, but such promise
may be too strong in many cases, so we need something more flexible
than the built-in address-of operator. Short forwarding lambdas would
be very helpful.
> [](auto&&... vs)
> { return id-expression(std::forward<decltype(vs)>(vs)...); }
>
> The above allows the user to pass any overloaded function or
> function template as if it were a function object, bypassing the
> main issues mentioned in the motivation. However, there are
> drawbacks:
>
> * Writing out the whole lambda expression is tedious and introduces
> unnecessary clutter.
We could have more short notation:
forwarding-lambda-expression:
lambda-introducer lambda-declarator opt =3D>
assignment-expression
assignment-expression:
forwarding-lambda-expression
....
[](auto&&... vs) =3D> id-expression(FORWARD(vs)...);
but
[] id-expression
is obviously more concise.
> * It only allows exactly that invocation form and dismisses member
> functions and member data.
I consider that as an advantage. If we want to use a class member, we
should express our intent explicitly. Moreover, an access through .
(dot) should be distinguishable from an access through -> (arrow).
For example,
x.reset()
and
x->reset()
may both be well-formed but have different meaning.
> * Operator support is incomplete, only allowing the non-member
> direct invocation syntax operator@(arguments...), ignoring member
> operator overloads as well as the special rules for operator
> function name look-up
I don't see problem here. We are free to change lambda definition
accordingly.
[](auto &&x) =3D> @FORWARD(x)
[](auto &&x) =3D> FORWARD(x)@
[](auto &&x, auto &&y) =3D> (FORWARD(x) @ FORWARD(y))
> These drawbacks, especially the third one, can not be solved in a
> "library"-way (say, with a macro).
That's a controversial opinion. We could use several macro
definitions:
#define FORWARD(x) static_cast<decltype(x) &&>(x)
#define FUNC(f) \
[](auto &&... params) =3D> f(FORWARD(params)...)
#define MEM_FUNC(f) \
[](auto &&obj, auto &&... params) =3D> \
FORWARD(obj).f(FORWARD(params)...)
#define INDIRECT_MEM_FUNC(f) \
[](auto &&obj, auto &&... params) =3D> \
FORWARD(obj)->f(FORWARD(params)...)
#define UNARY_OP(op) \
[](auto &&x) =3D> op FORWARD(x)
#define POSTFIX_OP(op) \
[](auto &&x) =3D> FORWARD(x) op
#define BINARY_OP(op) \
[](auto &&x, auto &&y) =3D> (FORWARD(x) op FORWARD(y))
#define SUBSCRIPT_OP \
[](auto &&x, auto &&y) =3D> FORWARD(x)[FORWARD(y)]
#define CALL_OP \
[](auto &&obj, auto &&... params) =3D> \
FORWARD(obj)(FORWARD(params)...)
I would still prefer core language solution though :-)
> The semantics of INVOKE seemed like a good starting point
From my point of view, contrived uniformity is an evil. I can assume
that fast-to-write-but-hard-to-read programs are what some people
really want, but I don't fall into this category. If we need to waste
a lot of time in order to figure out what exactly some simple code is
supposed to do, because we have to thoroughly investigate large context
and apply several disambiguation rules, then visual simplicity and
uniformity don't look so cute.
I would prefer to have different syntax for different use cases,
rather than to deal with dirty tricks like INVOKE:
1) []id_expression
=20
[](auto &&... params) =3D> id_expression(FORWARD(params)...)
2) [].id_expression
=20
[](auto &&x, auto &&... params) =3D>
FORWARD(x).id_expression(FORWARD(params)...)
3) []->id_expression
=20
[](auto &&x, auto &&... params) =3D>
FORWARD(x)->id_expression(FORWARD(params)...)
4) [] =3D .id_expression
=20
[](auto &&x) =3D> FORWARD(x).id_expression
5) [] =3D ->id_expression
=20
[](auto &&x) =3D> FORWARD(x)->id_expression
Advantages:
* this approach is less error-prone, because we explicitly state what
we want exactly, while implicit assumptions made by a too smart
compiler have more chances to be wrong (i.e. not match original
intent)
auto m =3D [] =3D .run;
auto c =3D [].run; =20
struct F
{
std::function<void()> run;
} f;
m(f) =3D []{ /*...*/ }; // assigns []{ /*...*/ } to f.run
c(f); // calls f.run()
* the semantics is transparent for readers;
* simple correspondence would (potentially) help compilers to provide
better diagnostic messages, because if our code is ill-formed, we
don't need (potentially long) explanation why 5 different
interpretations are wrong / ill-formed.
> An idea was to allow []obj.id-expression for this-binding (like
> std::bind) - []x.foo would then create a nullary lifting-lamba.
I think, it should be [=3D]x.foo (x is captured by copy) or [&]x.foo
(x is captured by reference).
--=20
---=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/?hl=3Den.
------=_Part_2056_30499971.1366792169907
Content-Type: text/html; charset=windows-1252
Content-Transfer-Encoding: quoted-printable
It's good to see that we have an official proposal about forwarding<br>lamb=
das. I was going to write similar paper, but now we can just<br>discuss thi=
s one.<br><br>> When dealing with generic algorithms, like the function =
templates in<br>> <algorithm>, it can be quite cumbersome to pass =
an overloaded<br>> function or a function template<br><br>... or a funct=
ion with default arguments:<br><br> std::wstring to_wstri=
ng(<br> std::string const &s,=
<br> encoding_t encoding =3D defa=
ult_encoding());<br><br> std::vector<std::string> s=
rc =3D source();<br> std::vector<std::wstring> dst;=
<br><br> std::transform(<br>  =
; src.begin(),<br> sr=
c.end(),<br> std::back_inserter(d=
st),<br> to_wstring); // won't wo=
rk<br><br>In some cases we can't safely use address of a member function (e=
ven<br>with further explicit conversions), because the function's type is n=
ot<br>fixed: according to C++11 - 17.6.5.5,<br><br> An im=
plementation may declare additional non-virtual member<br>  =
; function signatures within a class:<br><br> =
=97 by adding arguments with default values to a member functi=
on<br> signature; [Fo=
otnote: Hence, the address of a member<br> &nb=
sp; function of a class in the C++ standard library has a=
n<br> unspecified typ=
e.] [ Note: An implementation may not add<br> =
arguments with default values to virtual, global, =
or<br> non-member fun=
ctions.=97end note ]<br><br> =97 =
by replacing a member function signature with default values<br>  =
; by two or more member function =
signatures with equivalent<br> &nb=
sp; behavior; and<br><br> =
=97 by adding a member function signature for a member function<br> &n=
bsp; name.<br><br>In general, we =
can take address of only those functions which are<br>guarateed to have the=
same type (and also be non-overloaded<br>non-templates if we don't use fur=
ther casts) forever, but such promise<br>may be too strong in many cases, s=
o we need something more flexible<br>than the built-in address-of operator.=
Short forwarding lambdas would<br>be very helpful.<br><br>> [](auto&=
;&... vs)<br>> { return id-expression(std::forward<decltype(vs)&g=
t;(vs)...); }<br>><br>> The above allows the user to pass any overloa=
ded function or<br>> function template as if it were a function object, =
bypassing the<br>> main issues mentioned in the motivation. However, the=
re are<br>> drawbacks:<br>><br>> * Writing out the whole lambda ex=
pression is tedious and introduces<br>> unnecessary clutter.=
<br><br>We could have more short notation:<br><br> forwar=
ding-lambda-expression:<br> lambd=
a-introducer lambda-declarator opt =3D><br>  =
; assignment-expression<br><br> &n=
bsp; assignment-expression:<br> &n=
bsp; forwarding-lambda-expression<br> &n=
bsp; ....<br><br> [](auto&&... vs) =3D> id-exp=
ression(FORWARD(vs)...);<br><br>but<br><br> [] id-express=
ion<br><br>is obviously more concise.<br><br>> * It only allows exactly =
that invocation form and dismisses member<br>> functions and=
member data.<br><br>I consider that as an advantage. If we want to use a c=
lass member, we<br>should express our intent explicitly. Moreover, an acces=
s through .<br>(dot) should be distinguishable from an access through ->=
(arrow).<br>For example,<br><br> x.reset()<br><br>and<br=
><br> x->reset()<br><br>may both be well-formed but ha=
ve different meaning.<br><br>> * Operator support is incomplete, only al=
lowing the non-member<br>> direct invocation syntax operator=
@(arguments...), ignoring member<br>> operator overloads as =
well as the special rules for operator<br>> function name lo=
ok-up<br><br>I don't see problem here. We are free to change lambda definit=
ion<br>accordingly.<br><br> [](auto &&x) =3D> =
@FORWARD(x)<br> [](auto &&x) =3D> FORWARD(x)@<=
br> [](auto &&x, auto &&y) =3D> (FORWA=
RD(x) @ FORWARD(y))<br><br>> These drawbacks, especially the third one, =
can not be solved in a<br>> "library"-way (say, with a macro).<br><br>Th=
at's a controversial opinion. We could use several macro<br>definitions:<br=
><br> #define FORWARD(x) static_cast<decltype(x) &=
&>(x)<br><br> #define FUNC(f) \<br> &nb=
sp; [](auto &&... params) =3D> f(FORWARD=
(params)...)<br><br> #define MEM_FUNC(f) \<br>  =
; [](auto &&obj, auto &&... p=
arams) =3D> \<br> &=
nbsp; FORWARD(obj).f(FORWARD(params)...)<br><br> #d=
efine INDIRECT_MEM_FUNC(f) \<br> =
[](auto &&obj, auto &&... params) =3D> \<br> =
FORWARD(obj)->f(F=
ORWARD(params)...)<br><br> #define UNARY_OP(op) \<br>&nbs=
p; [](auto &&x) =3D> op FORW=
ARD(x)<br><br> #define POSTFIX_OP(op) \<br> &n=
bsp; [](auto &&x) =3D> FORWARD(x) op<br>=
<br> #define BINARY_OP(op) \<br> &=
nbsp; [](auto &&x, auto &&y) =3D> (FORWARD(x=
) op FORWARD(y))<br><br> #define SUBSCRIPT_OP \<br> =
[](auto &&x, auto &&y)=
=3D> FORWARD(x)[FORWARD(y)]<br><br> #define CALL_OP \=
<br> [](auto &&obj, auto =
&&... params) =3D> \<br> &nbs=
p; FORWARD(obj)(FORWARD(params)...)<br><br>I would =
still prefer core language solution though :-)<br><br>> The semantics of=
INVOKE seemed like a good starting point<br><br>From my point of view, con=
trived uniformity is an evil. I can assume<br>that fast-to-write-but-hard-t=
o-read programs are what some people<br>really want, but I don't fall into =
this category. If we need to waste<br>a lot of time in order to figure out =
what exactly some simple code is<br>supposed to do, because we have to thor=
oughly investigate large context<br>and apply several disambiguation rules,=
then visual simplicity and<br>uniformity don't look so cute.<br><br>I woul=
d prefer to have different syntax for different use cases,<br>rather than t=
o deal with dirty tricks like INVOKE:<br><br>1) []id_expression<br> &n=
bsp; <br> [](auto &&... params) =3D> id_=
expression(FORWARD(params)...)<br><br>2) [].id_expression<br> &=
nbsp;<br> [](auto &&x, auto &&... params)=
=3D><br> FORWARD(x).id_expres=
sion(FORWARD(params)...)<br><br>3) []->id_expression<br> &nb=
sp;<br> [](auto &&x, auto &&... params) =
=3D><br> FORWARD(x)->id_exp=
ression(FORWARD(params)...)<br><br>4) [] =3D .id_expression<br> =
<br> [](auto &&x) =3D> FORWARD(x).id_ex=
pression<br><br>5) [] =3D ->id_expression<br> <br>&nbs=
p; [](auto &&x) =3D> FORWARD(x)->id_expression<br=
><br>Advantages:<br><br>* this approach is less error-prone, because we exp=
licitly state what<br> we want exactly, while implicit assumptions ma=
de by a too smart<br> compiler have more chances to be wrong (i.e. no=
t match original<br> intent)<br><br> auto m =3D [] =
=3D .run;<br> auto c =3D [].run; <br><b=
r> struct F<br> {<br> =
std::function<void()> run;<br> &n=
bsp; } f;<br><br> m(f) =3D []{ /*...*/ }; // assign=
s []{ /*...*/ } to f.run<br> c(f); // calls f.run()=
<br><br>* the semantics is transparent for readers;<br><br>* simple corresp=
ondence would (potentially) help compilers to provide<br> better diag=
nostic messages, because if our code is ill-formed, we<br> don't need=
(potentially long) explanation why 5 different<br> interpretations a=
re wrong / ill-formed.<br><br>> An idea was to allow []obj.id-expression=
for this-binding (like<br>> std::bind) - []x.foo would then create a nu=
llary lifting-lamba.<br><br>I think, it should be [=3D]x.foo (x is captured=
by copy) or [&]x.foo<br>(x is captured by reference).
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
<br />
<br />
------=_Part_2056_30499971.1366792169907--
.
Author: Giovanni Piero Deretta <gpderetta@gmail.com>
Date: Wed, 24 Apr 2013 09:05:43 -0700 (PDT)
Raw View
------=_Part_3961_29805573.1366819543375
Content-Type: text/plain; charset=ISO-8859-1
On Wednesday, April 24, 2013 9:29:29 AM UTC+1, Nikolay Ivchenkov wrote:
>
> It's good to see that we have an official proposal about forwarding
> lambdas. I was going to write similar paper, but now we can just
> discuss this one.
>
>
[snip]
> > * It only allows exactly that invocation form and dismisses member
> > functions and member data.
>
> I consider that as an advantage. If we want to use a class member, we
> should express our intent explicitly. Moreover, an access through .
> (dot) should be distinguishable from an access through -> (arrow).
> For example,
>
> x.reset()
>
> and
>
> x->reset()
>
> may both be well-formed but have different meaning.
>
> [snip]
> > The semantics of INVOKE seemed like a good starting point
>
> From my point of view, contrived uniformity is an evil. I can assume
> that fast-to-write-but-hard-to-read programs are what some people
> really want, but I don't fall into this category. If we need to waste
> a lot of time in order to figure out what exactly some simple code is
> supposed to do, because we have to thoroughly investigate large context
> and apply several disambiguation rules, then visual simplicity and
> uniformity don't look so cute.
>
>
The uniformity is not just for its own sake. It is vital for generic code.
Think for example about std::begin/end which exist to bridge the gap
between containers having member functions and other ranges which do not
have them.
There has been a lot of talks in the past about unifying the function call
syntax, but nothing ever materialized. This proposal does it, plus a few
other useful additions.
-- gpd
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
------=_Part_3961_29805573.1366819543375
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
<br>On Wednesday, April 24, 2013 9:29:29 AM UTC+1, Nikolay Ivchenkov wrote:=
<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bor=
der-left: 1px #ccc solid;padding-left: 1ex;">It's good to see that we have =
an official proposal about forwarding<br>lambdas. I was going to write simi=
lar paper, but now we can just<br>discuss this one.<br><br></blockquote><di=
v><br>[snip] <br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0=
;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">> * =
It only allows exactly that invocation form and dismisses member<br>>&nb=
sp; functions and member data.<br><br>I consider that as an advantage=
.. If we want to use a class member, we<br>should express our intent explici=
tly. Moreover, an access through .<br>(dot) should be distinguishable from =
an access through -> (arrow).<br>For example,<br><br> =
x.reset()<br><br>and<br><br> x->reset()<br><br>may bot=
h be well-formed but have different meaning.<br><br></blockquote><div>[snip=
]<br></div><blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left=
: 0.8ex;border-left: 1px #ccc solid;padding-left: 1ex;">> The semantics =
of INVOKE seemed like a good starting point<br><br>From my point of view, c=
ontrived uniformity is an evil. I can assume<br>that fast-to-write-but-hard=
-to-read programs are what some people<br>really want, but I don't fall int=
o this category. If we need to waste<br>a lot of time in order to figure ou=
t what exactly some simple code is<br>supposed to do, because we have to th=
oroughly investigate large context<br>and apply several disambiguation rule=
s, then visual simplicity and<br>uniformity don't look so cute.<br><br></bl=
ockquote><div><br>The uniformity is not just for its own sake. It is vital =
for generic code. Think for example about std::begin/end which exist to bri=
dge the gap between containers having member functions and other ranges whi=
ch do not have them.<br><br>There has been a lot of talks in the past about=
unifying the function call syntax, but nothing ever materialized. This pro=
posal does it, plus a few other useful additions.<br><br>-- gpd<br></div><=
br>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
<br />
<br />
------=_Part_3961_29805573.1366819543375--
.
Author: Nikolay Ivchenkov <mk.ivchenkov@gmail.com>
Date: Wed, 24 Apr 2013 10:16:04 -0700 (PDT)
Raw View
------=_Part_2303_12658887.1366823764819
Content-Type: text/plain; charset=ISO-8859-1
---------- Forwarded message ----------
From: Xeo <...>
Date: Wed, Apr 24, 2013 at 1:16 PM
Subject: Re: Making C++ more functional
On Wednesday, April 24, 2013 10:29:29 AM UTC+2, Nikolay Ivchenkov wrote:
>
>
> ... or a function with default arguments:
>
Ah, I knew I forgot something in my list. Also, I'll steal that example.
> In some cases we can't safely use address of a member function (even
> with further explicit conversions), because the function's type is not
> fixed: according to C++11 - 17.6.5.5
I didn't know that, another good example!
> * It only allows exactly that invocation form and dismisses member
> > functions and member data.
>
> I consider that as an advantage. If we want to use a class member, we
> should express our intent explicitly. Moreover, an access through .
> (dot) should be distinguishable from an access through -> (arrow).
> For example,
>
> x.reset()
>
> and
>
> x->reset()
>
> may both be well-formed but have different meaning.
>
A good point. And pulling up something further down:
> The semantics of INVOKE seemed like a good starting point
>
> From my point of view, contrived uniformity is an evil. I can assume
> that fast-to-write-but-hard-to-read programs are what some people
> really want, but I don't fall into this category.
>
As I said, it was a *starting point*. It wasn't meant to be the end of it
all. I just had to start somewhere.
> I don't see problem here. We are free to change lambda definition
> accordingly.
>
> [](auto &&x) => @FORWARD(x)
> [](auto &&x) => FORWARD(x)@
> [](auto &&x, auto &&y) => (FORWARD(x) @ FORWARD(y))
>
I don't quite get what exactly you're on to? (Also, is FORWARD supposed to
be a `#define FORWARD(x) std::forward<decltype(x)>(x)`? If yes -> sad
panda.)
I would prefer to have different syntax for different use cases,
> rather than to deal with dirty tricks like INVOKE:
>
A thought that was lingering in my mind was to let this also enable a
uniform calling syntax, but in hindsight that may be conflating unrelated
concepts.
> I think, it should be [=]x.foo (x is captured by copy) or [&]x.foo
> (x is captured by reference).
What about [x].foo or [&x].foo? :) Just food for thought.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
------=_Part_2303_12658887.1366823764819
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
---------- Forwarded message ----------<br>From: <b class=3D"gmail_senderna=
me">Xeo</b> <span dir=3D"ltr"><...></span><br>Date: Wed, Apr 24, 2013=
at 1:16 PM<br>Subject: Re: Making C++ more functional<br><br>On Wednesday,=
April 24, 2013 10:29:29 AM UTC+2, Nikolay Ivchenkov wrote:<blockquote clas=
s=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc =
solid;padding-left:1ex"><br>... or a function with default arguments:<br></=
blockquote><div> </div><div>Ah, I knew I forgot something in my list. =
Also, I'll steal that example.<br></div><div> </div><blockquote class=
=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc s=
olid;padding-left:1ex">In some cases we can't safely use address of a membe=
r function (even<br>with further explicit conversions), because the functio=
n's type is not<br>fixed: according to C++11 - 17.6.5.5 </blockquote><=
div><br>I didn't know that, another good example!<br> <br></div><blockquote=
class=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px =
#ccc solid;padding-left:1ex">> * It only allows exactly that invocation =
form and dismisses member<br>> functions and member data.<br=
><br>I consider that as an advantage. If we want to use a class member, we<=
br>should express our intent explicitly. Moreover, an access through .<br>(=
dot) should be distinguishable from an access through -> (arrow).<br>For=
example,<br><br> x.reset()<br><br>and<br><br>  =
; x->reset()<br><br>may both be well-formed but have different mea=
ning.<br></blockquote><div><br>A good point. And pulling up something furth=
er down:<br><br><blockquote style=3D"margin:0px 0px 0px 0.8ex;border-left:1=
px solid rgb(204,204,204);padding-left:1ex" class=3D"gmail_quote">> The =
semantics of INVOKE seemed like a good starting point<br><br>From my point =
of view, contrived uniformity is an evil. I can assume<br>that fast-to-writ=
e-but-hard-to-read programs are what some people<br>really want, but I don'=
t fall into this category.<br></blockquote><div><br>As I said, it was a *st=
arting point*. It wasn't meant to be the end of it all. I just had to start=
somewhere. <br></div> <br></div><blockquote class=3D"gmail_quote" sty=
le=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-left:1e=
x">I don't see problem here. We are free to change lambda definition<br>acc=
ordingly.<br><br> [](auto &&x) =3D> @FORWARD(x=
)<br> [](auto &&x) =3D> FORWARD(x)@<br> &=
nbsp; [](auto &&x, auto &&y) =3D> (FORWARD(x) @ FO=
RWARD(y))<br></blockquote><div><br>I don't quite get what exactly you're on=
to? (Also, is FORWARD supposed to be a `#define FORWARD(x) std::forward<=
;decltype(x)>(x)`? If yes -> sad panda.)<br><br></div><blockquote cla=
ss=3D"gmail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc=
solid;padding-left:1ex">I would prefer to have different syntax for differ=
ent use cases,<br>rather than to deal with dirty tricks like INVOKE:<br></b=
lockquote><div><br>A thought that was lingering in my mind was to let this =
also enable a uniform calling syntax, but in hindsight that may be conflati=
ng unrelated concepts.<br></div><div> <br></div><blockquote class=3D"g=
mail_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;=
padding-left:1ex">I think, it should be [=3D]x.foo (x is captured by copy) =
or [&]x.foo<br>(x is captured by reference).</blockquote><div><br>What =
about [x].foo or [&x].foo? :) Just food for thought.</div><br>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
<br />
<br />
------=_Part_2303_12658887.1366823764819--
.
Author: Nikolay Ivchenkov <mk.ivchenkov@gmail.com>
Date: Wed, 24 Apr 2013 10:22:53 -0700 (PDT)
Raw View
------=_Part_4234_2578918.1366824173463
Content-Type: text/plain; charset=ISO-8859-1
On Wed, Apr 24, 2013 at 1:16 PM, Xeo <...> wrote:
>
> I don't see problem here. We are free to change lambda definition
>> accordingly.
>>
>> [](auto &&x) => @FORWARD(x)
>> [](auto &&x) => FORWARD(x)@
>> [](auto &&x, auto &&y) => (FORWARD(x) @ FORWARD(y))
>>
>
> I don't quite get what exactly you're on to?
>
I want to say that an operator invocation (as well as an operator
function invocation of any form) can be wrapped in a polymorphic
lambda with explicitly provided parameters. The resulting expression
would be far from being terse, but we can get desirable functionality;
therefore, I don't see a problem in this part:
"Operator support is incomplete, only allowing the non-member
direct invocation syntax operator@(arguments...), ignoring member
operator overloads as well as the special rules for operator
function name look-up."
> (Also, is FORWARD supposed to be a `#define FORWARD(x)
> std::forward<decltype(x)>(x)`?
>
My definition is
#define FORWARD(x) static_cast<decltype(x) &&>(x)
I think, it should be [=]x.foo (x is captured by copy) or [&]x.foo
>> (x is captured by reference).
>
>
> What about [x].foo or [&x].foo? :) Just food for thought.
>
We may get parsing issues then. For example,
(Type())[x].foo
may be a valid expression under C++98/03/11 rules. If we assume that
[x].foo could be a primary-expression, then we'll get new possible
interpretation: it is (semantically incorrect) cast-expression, where
[x].foo is converted to type Type(), and such ill-formed
interpretation shall be chosen according to 8.2[dcl.ambig.res]/2.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
------=_Part_4234_2578918.1366824173463
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
On Wed, Apr 24, 2013 at 1:16 PM, Xeo <span dir=3D"ltr"><...></span> w=
rote:<br><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .8ex;borde=
r-left:1px #ccc solid;padding-left:1ex"><br><blockquote class=3D"gmail_quot=
e" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padding-l=
eft:1ex">I don't see problem here. We are free to change lambda definition<=
br>accordingly.<br><br> [](auto &&x) =3D> @FOR=
WARD(x)<br> [](auto &&x) =3D> FORWARD(x)@<br>&=
nbsp; [](auto &&x, auto &&y) =3D> (FORWARD(x=
) @ FORWARD(y))<br></blockquote><div><br>I don't quite get what exactly you=
're on to?</div></blockquote><div><br></div><div>I want to say that an oper=
ator invocation (as well as an operator<br>function invocation of any form)=
can be wrapped in a polymorphic<br>lambda with explicitly provided paramet=
ers. The resulting expression<br>would be far from being terse, but we can =
get desirable functionality;<br>therefore, I don't see a problem in this pa=
rt:<br><br> "Operator support is incomplete, only allowin=
g the non-member<br> direct invocation syntax operator@(argu=
ments...), ignoring member<br> operator overloads as well as=
the special rules for operator<br> function name look-up."<=
/div><div> </div><blockquote class=3D"gmail_quote" style=3D"margin:0 0=
0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div> (Also, is FORWAR=
D supposed to be a `#define FORWARD(x) std::forward<decltype(x)>(x)`?=
</div></blockquote><div><br></div><div>My definition is<br><br> &nb=
sp; #define FORWARD(x) static_cast<decltype(x) &&>(x)</div><d=
iv><br></div><div><blockquote class=3D"gmail_quote" style=3D"margin:0 0 0 .=
8ex;border-left:1px #ccc solid;padding-left:1ex"><blockquote class=3D"gmail=
_quote" style=3D"margin:0;margin-left:0.8ex;border-left:1px #ccc solid;padd=
ing-left:1ex">I think, it should be [=3D]x.foo (x is captured by copy) or [=
&]x.foo<br>(x is captured by reference).</blockquote><div><br>What abou=
t [x].foo or [&x].foo? :) Just food for thought.<br></div></blockquote>=
</div><div><br></div><div>We may get parsing issues then. For example,<br><=
br> (Type())[x].foo<br><br>may be a valid expression under C=
++98/03/11 rules. If we assume that<br>[x].foo could be a primary-expressio=
n, then we'll get new possible<br>interpretation: it is (semantically incor=
rect) cast-expression, where<br>[x].foo is converted to type Type(), and su=
ch ill-formed<br>interpretation shall be chosen according to 8.2[dcl.ambig.=
res]/2.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
<br />
<br />
------=_Part_4234_2578918.1366824173463--
.
Author: Nikolay Ivchenkov <mk.ivchenkov@gmail.com>
Date: Wed, 24 Apr 2013 10:56:24 -0700 (PDT)
Raw View
------=_Part_3500_7685266.1366826184736
Content-Type: text/plain; charset=ISO-8859-1
On Wednesday, April 24, 2013 8:05:43 PM UTC+4, Giovanni Piero Deretta wrote:
>
>
> The uniformity is not just for its own sake. It is vital for generic code.
> Think for example about std::begin/end which exist to bridge the gap
> between containers having member functions and other ranges which do not
> have them.
>
IMO, the analogy with std::begin/end is wrong. There is nothing bad in
overloading when it allows to perform *semantically equivalent/similar*operations uniformly (as std::begin/std::end do). INVOKE is a sort of dirty
overloading, where *semantically different* operations are combined in a
single notation. No sane generic code should rely on such kind of
uniformity. We already have list-initialization, which often dramatically
reduces readability (in some cases even language lawyers can't figure out
what's going on). I don't want to get another such thing - I prefer to read
code, rather than to decrypt it.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
------=_Part_3500_7685266.1366826184736
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
On Wednesday, April 24, 2013 8:05:43 PM UTC+4, Giovanni Piero Deretta wrote=
:<blockquote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;bo=
rder-left: 1px #ccc solid;padding-left: 1ex;"><br>The uniformity is not jus=
t for its own sake. It is vital for generic code. Think for example about s=
td::begin/end which exist to bridge the gap between containers having membe=
r functions and other ranges which do not have them.<br></blockquote><div><=
br></div><div>IMO, the analogy with std::begin/end is wrong. There is =
nothing bad in overloading when it allows to perform <strong>semantically e=
quivalent/similar</strong> operations uniformly (as std::begin/std::end do)=
.. INVOKE is a sort of dirty overloading, where <strong>semantically differe=
nt</strong> operations are combined in a single notation. No sane generic c=
ode should rely on such kind of uniformity. We already have list-initializa=
tion, which often dramatically reduces readability (in some cases even lang=
uage lawyers can't figure out what's going on). I don't want to get another=
such thing - I prefer to read code, rather than to decrypt it.</div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
<br />
<br />
------=_Part_3500_7685266.1366826184736--
.
Author: Xeo <hivemaster@hotmail.de>
Date: Wed, 24 Apr 2013 12:27:26 -0700 (PDT)
Raw View
------=_Part_35_17824567.1366831646789
Content-Type: text/plain; charset=ISO-8859-1
Thanks for forwarding my mail! :)
On Wednesday, April 24, 2013 7:22:53 PM UTC+2, Nikolay Ivchenkov wrote:
>
>
> I want to say that an operator invocation (as well as an operator
> function invocation of any form) can be wrapped in a polymorphic
> lambda with explicitly provided parameters. The resulting expression
> would be far from being terse, but we can get desirable functionality;
> therefore, I don't see a problem in this part:
>
Ah, but I meant when wrapped in a single macro.
> (Also, is FORWARD supposed to be a `#define FORWARD(x)
>> std::forward<decltype(x)>(x)`?
>>
>
> My definition is
>
> #define FORWARD(x) static_cast<decltype(x) &&>(x)
>
/sadpanda :(
> We may get parsing issues then. For example,
>
> (Type())[x].foo
>
> may be a valid expression under C++98/03/11 rules. If we assume that
> [x].foo could be a primary-expression, then we'll get new possible
> interpretation: it is (semantically incorrect) cast-expression, where
> [x].foo is converted to type Type(), and such ill-formed
> interpretation shall be chosen according to 8.2[dcl.ambig.res]/2.
>
Okay, that is interesting. I'll definitly add `[=]x.foo` and `[&]x.foo` as
considerations.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/?hl=en.
------=_Part_35_17824567.1366831646789
Content-Type: text/html; charset=ISO-8859-1
Content-Transfer-Encoding: quoted-printable
Thanks for forwarding my mail! :)<br><br>On Wednesday, April 24, 2013 7:22:=
53 PM UTC+2, Nikolay Ivchenkov wrote:<blockquote class=3D"gmail_quote" styl=
e=3D"margin: 0;margin-left: 0.8ex;border-left: 1px #ccc solid;padding-left:=
1ex;"><br><div>I want to say that an operator invocation (as well as an op=
erator<br>function invocation of any form) can be wrapped in a polymorphic<=
br>lambda with explicitly provided parameters. The resulting expression<br>=
would be far from being terse, but we can get desirable functionality;<br>t=
herefore, I don't see a problem in this part:</div></blockquote><div><br>Ah=
, but I meant when wrapped in a single macro.<br> </div><blockquote cl=
ass=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-left: 1px =
#ccc solid;padding-left: 1ex;"><blockquote class=3D"gmail_quote" style=3D"m=
argin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div> (Also, =
is FORWARD supposed to be a `#define FORWARD(x) std::forward<decltype(x)=
>(x)`?</div></blockquote><div><br></div><div>My definition is<br><br>&nb=
sp; #define FORWARD(x) static_cast<decltype(x) &&>(x=
)</div></blockquote><div><br>/sadpanda :( <br></div><div> </div><block=
quote class=3D"gmail_quote" style=3D"margin: 0;margin-left: 0.8ex;border-le=
ft: 1px #ccc solid;padding-left: 1ex;"><div>We may get parsing issues then.=
For example,<br><br> (Type())[x].foo<br><br>may be a valid =
expression under C++98/03/11 rules. If we assume that<br>[x].foo could be a=
primary-expression, then we'll get new possible<br>interpretation: it is (=
semantically incorrect) cast-expression, where<br>[x].foo is converted to t=
ype Type(), and such ill-formed<br>interpretation shall be chosen according=
to 8.2[dcl.ambig.res]/2.<br></div></blockquote><div> <br>Okay, that i=
s interesting. I'll definitly add `[=3D]x.foo` and `[&]x.foo` as consid=
erations.<br></div>
<p></p>
-- <br />
<br />
--- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals" group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.<br />
To post to this group, send email to std-proposals@isocpp.org.<br />
Visit this group at <a href=3D"http://groups.google.com/a/isocpp.org/group/=
std-proposals/?hl=3Den">http://groups.google.com/a/isocpp.org/group/std-pro=
posals/?hl=3Den</a>.<br />
<br />
<br />
------=_Part_35_17824567.1366831646789--
.