{"id":1804,"date":"2025-10-10T16:06:25","date_gmt":"2025-10-10T22:06:25","guid":{"rendered":"https:\/\/www.elbeno.com\/blog\/?p=1804"},"modified":"2025-10-10T16:33:03","modified_gmt":"2025-10-10T22:33:03","slug":"functions-are-asymmetric","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1804","title":{"rendered":"Functions are Asymmetric"},"content":{"rendered":"\n<p>Here are some functions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>auto f() -&gt; int;\nauto g(int) -&gt; int;\nauto h(int, int) -&gt; int;<\/code><\/pre>\n\n\n\n<p>What they all have in common, structurally, is that they each return one thing. Even though they may take any number of arguments, they must each have <em>one<\/em> return value. One shall be the number, and the number shall be one. This is true of all functions.<\/p>\n\n\n\n<p>(It&#8217;s not too important, but here I consider <code>void<\/code> to be &#8220;one thing&#8221; as a return type, for reasons which will hopefully become clear. Zero is right out.)<\/p>\n\n\n\n<p>Maybe that doesn&#8217;t seem strange. It&#8217;s just how we&#8217;re used to functions working. Ah, those steeped in functional programming might say, but maybe this is the wrong way to look at it. Because if we <em>curry<\/em> functions and use partial application, we can say that they <em>always<\/em> have one argument and return one value, and then they <em>are<\/em> symmetric.<\/p>\n\n\n\n<p>Well <a href=\"https:\/\/youtu.be\/ojZbFIQSdl8?t=790\" data-type=\"link\" data-id=\"https:\/\/youtu.be\/ojZbFIQSdl8?t=790\">that&#8217;s mathematically true<\/a>. This is how some languages &#8212; like Haskell &#8212; work, and they get along fine, having built-in partial application and lots of tools to deal with that view. Most languages &#8212; like C++ &#8212; don&#8217;t have that wealth of tools and it&#8217;s more natural to express multiple-argument functions. Especially because we have different tools to deal with variadic functions. Treating functions like they take multiple values is <em>fine<\/em>.<\/p>\n\n\n\n<p>OK, rather than reducing every function to one argument, alternatively we can try to attack this problem by returning multiple values as one, in a <code>tuple<\/code> (or other product type), and we could make that work. That would be perfectly doable, if tedious.<\/p>\n\n\n\n<p>So why bring this up at all? Because the title is missing a word. It should say &#8220;<em>Synchronous<\/em> Functions are Asymmetric&#8221;. Because when we move to <a href=\"https:\/\/cppreference.com\/w\/cpp\/experimental\/execution.html\" data-type=\"link\" data-id=\"https:\/\/cppreference.com\/w\/cpp\/experimental\/execution.html\">asynchronous functions<\/a>, they needn&#8217;t be! But implementing them with synchronous functions &#8212; as we must &#8212; can impose asymmetry.<\/p>\n\n\n\n<p>Here are some asynchronous functions analogous to the functions above:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>auto f = then(&#91;] { return 42; });\nauto g = then(&#91;] (int x) { return x + 1; });\nauto h = then(\n  &#91;] (int x, int y) { return x + y; });<\/code><\/pre>\n\n\n\n<p>These functions are in their &#8220;pipeable&#8221; form here without their inputs. They could occur in expressions like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>just(42) \n  | then(&#91;] (int x) { return x + 1; }) \n  | ...<\/code><\/pre>\n\n\n\n<p>They receive their inputs on the value channel, and they send their result on the value channel. But the channel can contain any number of values:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>just(17, 42)\n  | then(&#91;] (int x, int y) { return x + y; }) \n  | ...<\/code><\/pre>\n\n\n\n<p>And now we see the artificial constraint better: because <code>then<\/code> is implemented using a single synchronous function, it <em>can&#8217;t<\/em> send multiple values. The asymmetry of synchronous functions is causing the same annoying asymmetry in the asynchronous world.<\/p>\n\n\n\n<p>We could of course do the same thing as in the synchronous case and return a <code>tuple<\/code> from the function passed to <code>then<\/code>. And we could even provide machinery that unwraps <code>tuple<\/code>s automatically into a pack of arguments that continue down the asynchronous pipeline. But that seems like a poor solution: for instance, what if we actually want to send a <code>tuple<\/code>? In that direction lies more complexity.<\/p>\n\n\n\n<p>If we do go down that road though, we can make it sound. We&#8217;re pushing the symmetry into the argument\/return type(s), and the logical place to end up is with every function both taking and returning one blessed structured value which represents a sum-of-products-of-values. <a href=\"http:\/\/www.youtube.com\/watch?v=bHxvfwTnJhg\" data-type=\"link\" data-id=\"www.youtube.com\/watch?v=bHxvfwTnJhg\">This approach exists<\/a>.<\/p>\n\n\n\n<p>Alternatively (and perhaps closer to what we are used to), we could allow <code>then<\/code> to take more (synchronous) functions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>just(A{}, B{}) \n  | then(&#91;] (A x) { ... },\n         &#91;] (B y) { ... })\n  | ...<\/code><\/pre>\n\n\n\n<p>And each such function gets to send its own value onward.<\/p>\n\n\n\n<p>Note: we aren&#8217;t doing this only to distribute the input arguments &#8212; if we wanted that, we could just do that by passing <code>then<\/code> a single function that would call more functions, distributing the arguments. We are doing this so that <code>then<\/code> itself can send more than one value.<\/p>\n\n\n\n<p><code><strong>call_by_need<\/strong><\/code><\/p>\n\n\n\n<p>(Note: &#8220;call by need&#8221; is a technique sometimes used to describe lazy evaluation. That is not how I am using it here. It just seems like a reasonable name for this function.)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;callable ...Fs, typename ...Args&gt;\nauto call_by_need(some&lt;Fs...&gt;, some&lt;Args...&gt;)\n  \/* -&gt; some&lt;Results...&gt; *\/;<\/code><\/pre>\n\n\n\n<p><code>call_by_need<\/code> takes some functions and some arguments, and applies the functions to the arguments, returning the results. In order to implement this, we need to define semantics for <em>how<\/em> the functions will be called.<\/p>\n\n\n\n<p>(Because of C++, we are currently limited to using <code>tuple<\/code> or equivalent machinery here, but that implementation detail isn&#8217;t going to show up in <code>then<\/code>.)<\/p>\n\n\n\n<p>How does <code>call_by_need<\/code> call the functions? As it can &#8212; and according to the following algorithm:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>For each function:\n<ul class=\"wp-block-list\">\n<li>try to call it with all arguments <code>[0..N)<\/code>.<\/li>\n\n\n\n<li>if that fails, try to call it with arguments <code>[0..N-1)<\/code>, etc, until a call succeeds.<\/li>\n\n\n\n<li>if <em>that<\/em> fails, try again starting with arguments <code>[1..N)<\/code>, etc.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>The results of the discovered function calls (with <code>void<\/code>s filtered out) become the results we&#8217;ll return.<\/li>\n\n\n\n<li>Finally, any unused arguments are appended to the results.<\/li>\n<\/ol>\n\n\n\n<p>In other words: we call each function with the earliest, longest sequence of arguments from the input list that is possible. This is just one possible approach, but a reasonable one.<\/p>\n\n\n\n<p>The explanation is clearer with some examples.<\/p>\n\n\n\n<p>The &#8220;normal&#8221; case:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>call_by_need(tuple{&#91;] (A) { return X{}; },\n                   &#91;] (B) { return Y{}; }},\n             tuple{A{}, B{}});\n\/\/ returns tuple&lt;X, Y&gt;<\/code><\/pre>\n\n\n\n<p>Swapping argument order need not alter the result order:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>call_by_need(tuple{&#91;] (A) { return X{}; },\n                   &#91;] (B) { return Y{}; }},\n             tuple{B{}, A{}});\n\/\/ returns tuple&lt;X, Y&gt;<\/code><\/pre>\n\n\n\n<p>But swapping function order does:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>call_by_need(tuple{&#91;] (B) { return Y{}; },\n                   &#91;] (A) { return X{}; }},\n             tuple{A{}, B{}});\n\/\/ returns tuple&lt;Y, X&gt;<\/code><\/pre>\n\n\n\n<p>Functions draw contiguous arguments, and arguments can be used multiple times:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>call_by_need(tuple{&#91;] (A, B) { return X{}; },\n                   &#91;] (B, C) { return Y{}; }},\n             tuple{A{}, B{}, C{}});\n\/\/ returns tuple&lt;X, Y&gt;<\/code><\/pre>\n\n\n\n<p>Any unused arguments are passed through <em>after<\/em> the function results:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>call_by_need(tuple{&#91;] (B) { return X{}; },\n                   &#91;] (C) { return Y{}; }},\n             tuple{A{}, B{}, C{}});\n\/\/ returns tuple&lt;X, Y, A&gt;<\/code><\/pre>\n\n\n\n<p>Functions can return <code>void<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>call_by_need(tuple{&#91;] (A) { return X{}; },\n                   &#91;] (B) { return; }},\n             tuple{A{}, B{}});\n\/\/ returns tuple&lt;X&gt;<\/code><\/pre>\n\n\n\n<p>If a function can&#8217;t be called, it&#8217;s an error:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>call_by_need(tuple{&#91;] (A) { return X{}; },\n                   &#91;] (B) { return Y{}; }},\n             tuple{A{}});\n\/\/ error! can't call second function<\/code><\/pre>\n\n\n\n<p>And functions with default arguments work normally.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>call_by_need(tuple{&#91;] (A) { return X{}; },\n                   &#91;] (B={}) { return Y{}; }},\n             tuple{A{}});\n\/\/ returns tuple&lt;X, Y&gt;<\/code><\/pre>\n\n\n\n<p><strong>Symmetric <code>then<\/code><\/strong><\/p>\n\n\n\n<p>Now that we have <code>call_by_need<\/code> (implementation left as an exercise), we can use it to implement a <code>then<\/code> that is symmetric.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>just(A{}, B{}) \n  | then(&#91;] (A x) { ... },\n         &#91;] (B y) { ... },\n         &#91;] { ... })\n  | ...<\/code><\/pre>\n\n\n\n<p>By allowing passing multiple synchronous functions, we also allow returning multiple results into the channel. Symmetry is restored. <\/p>\n\n\n\n<p>One interesting thing that comes out of this is that of course multiple results can mean no results. We can call <code>then<\/code> with no arguments:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>just(A{}, B{}) | then() | ...<\/code><\/pre>\n\n\n\n<p>And it acts like the identity function, leaving everything alone in the channel. I take this as a sign of good compositionality. This may seem useless, but such things always do at first. For one related use case, consider using <code>then<\/code> to insert side-effectful code while passing through arguments:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>just(A{}, B{}) \n  | then(&#91;] { print(\"hello\"); }) \n  | ...<\/code><\/pre>\n\n\n\n<p>Given a channel that can contain multiple values, it makes sense for a function acting on that channel that not only can it <em>take<\/em> multiple values, but that it can also <em>return<\/em> multiple values. We are limited by C++ in the ways we can actually implement that, but I think this formulation of <code>then<\/code> is not bad.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here are some functions: What they all have in common, structurally, is that they each return one thing. Even though they may take any number of arguments, they must each have one return value. One shall be the number, and the number shall be one. This is true of all&#8230;<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[22],"tags":[],"class_list":["post-1804","post","type-post","status-publish","format-standard","hentry","category-cpp"],"_links":{"self":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1804","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1804"}],"version-history":[{"count":6,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1804\/revisions"}],"predecessor-version":[{"id":1811,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1804\/revisions\/1811"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1804"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1804"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1804"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}