{"id":1813,"date":"2026-03-04T08:01:34","date_gmt":"2026-03-04T15:01:34","guid":{"rendered":"https:\/\/www.elbeno.com\/blog\/?p=1813"},"modified":"2026-03-04T08:37:49","modified_gmt":"2026-03-04T15:37:49","slug":"c-reflection-another-monad","status":"publish","type":"post","link":"https:\/\/www.elbeno.com\/blog\/?p=1813","title":{"rendered":"C++ Reflection: Another Monad"},"content":{"rendered":"\n<p>&#8220;Discovery consists of seeing what everybody has seen, and thinking what nobody has thought.&#8221;<\/p>\n\n\n\n<p>&#8212; Albert Szent-Gy\u00f6rgi, 1937 Nobel Laureate for Medicine<\/p>\n\n\n\n<p>I&#8217;m sure others <em>have<\/em> thought this, but they&#8217;re certainly not saying much about it. <a href=\"https:\/\/brevzin.github.io\/c++\/2026\/03\/02\/power-of-substitute\/\" data-type=\"link\" data-id=\"https:\/\/brevzin.github.io\/c++\/2026\/03\/02\/power-of-substitute\/\">Barry&#8217;s recent blog post<\/a> &#8220;Behold the power of <code>meta::substitute<\/code>&#8221; got so close to saying the words&#8230; in fact he even said, &#8220;the substitute\/extract\/invoke dance is remarkably powerful.&#8221;<\/p>\n\n\n\n<p>So I&#8217;m going to say it plainly: yes! Because <code>substitute<\/code> is basically <code>fmap<\/code>, and <code>extract<\/code> can implement <code>join<\/code>. Among other things, reflection gives us the monad for type function composition.<\/p>\n\n\n\n<p>To be a monad, we need three things: <code>pure<\/code> (\/ <code>return<\/code>), <code>fmap<\/code> and <code>bind<\/code> or <code>join<\/code>.<\/p>\n\n\n\n<p><code>pure :: a -&gt; m a<\/code><br>This is just the reflection operator <code>^^<\/code>. It takes a type from &#8220;type-land&#8221; into &#8220;reflection-land&#8221;. As a type function:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;typename T&gt;\nconstexpr auto pure = ^^T;<\/code><\/pre>\n\n\n\n<p><code>fmap :: (a -&gt; b) -&gt; f a -&gt; f b<\/code><br>This is basically <code>substitute<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;template &lt;typename...&gt; typename F&gt;\nconsteval auto fmap(std::same_as&lt;std::meta::info&gt; auto... as) {\n  return substitute(^^F, {as...});\n}<\/code><\/pre>\n\n\n\n<p>In fact, <code>substitute<\/code> is a little more than unary <code>fmap<\/code>, it&#8217;s n-ary <code>fmap<\/code>, which is equivalent to <code>&lt;*&gt;<\/code> (&#8220;ap&#8221;). So <code>substitute<\/code> gives us not only the functor, but the applicative functor.<\/p>\n\n\n\n<p><code>join :: m (m a) -&gt; m a<\/code><br><code>bind :: (a -&gt; m b) -&gt; m a -&gt; m b<\/code><\/p>\n\n\n\n<p>We have <code>join<\/code> pretty directly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>consteval auto join(std::meta::info a) {\n  return extract&lt;std::meta::info&gt;(a);\n}<\/code><\/pre>\n\n\n\n<p>And given <a href=\"http:\/\/wg21.link\/p2841\">P2841<\/a> &#8212; which is also in C++26 &#8212; we can write <code>bind<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>template &lt;template &lt;typename&gt; auto F&gt;\nconsteval auto bind(std::meta::info a) {\n  return join(substitute(^^F, {a}));\n}<\/code><\/pre>\n\n\n\n<p>All good. But hold on a minute &#8212; we also need to satisfy <a href=\"https:\/\/wiki.haskell.org\/index.php?title=Monad_laws\">a few laws<\/a>. To do that we need a useful definition of equality, which I&#8217;m going to take as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>consteval auto equal(std::meta::info a, std::meta::info b) {\n  return dealias(a) == dealias(b);\n}<\/code><\/pre>\n\n\n\n<p>For some reason, reflection deviates from the existing language behaviour with respect to aliases. Perhaps the authors of reflection were concerned with not losing information, or perhaps there is some warty technical reason buried in C++. But at any rate, the language surface up until now does not give us alias differences, so I&#8217;m going with alias equality.<\/p>\n\n\n\n<p>Back to the laws &#8212; left identity:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ return a &gt;&gt;= h &lt;=&gt; h a\n\ntemplate &lt;template &lt;typename&gt; auto F, typename T&gt;\nconsteval auto left_id() {\n  return equal(bind&lt;F&gt;(pure&lt;T&gt;), F&lt;T&gt;);\n}<\/code><\/pre>\n\n\n\n<p>And right identity:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ m >>= return &lt;=> m\n\ntemplate &lt;std::meta::info M>\nconsteval auto right_id() {\n  return equal(bind&lt;pure>(M), M);\n}<\/code><\/pre>\n\n\n\n<p>The <code>fmap<\/code> law, for which proof we need an &#8220;old-style&#8221; composition helper:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ xs &gt;&gt;= return . f &lt;=&gt; fmap f xs\n\ntemplate &lt;template &lt;typename...&gt; typename F&gt;\nstruct pure_dot_f {\n  template &lt;typename... Ts&gt;\n  constexpr static auto fn = pure&lt;F&lt;Ts...&gt;&gt;;\n};\n\ntemplate &lt;template &lt;typename...&gt; typename F, typename... Ts&gt;\nconsteval auto fmap_law() {\n  return equal(bind&lt;pure_dot_f&lt;F&gt;::template fn&gt;(^^Ts...), fmap&lt;F&gt;(^^Ts...));\n}<\/code><\/pre>\n\n\n\n<p>Of course the composition helper is just to show that this is correct according to &#8220;the old ways&#8221;, which are clunky. Where we&#8217;re going, we won&#8217;t need it any more.<\/p>\n\n\n\n<p>And finally the associativity law &#8212; once again, with an old-style helper:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ (m &gt;&gt;= g) &gt;&gt;= h &lt;=&gt; m &gt;&gt;= (\\x -&gt; g x &gt;&gt;= h)\n\ntemplate &lt;template &lt;typename&gt; auto G, template &lt;typename&gt; auto H&gt;\nstruct assoc_rhs_part {\n  template &lt;typename X&gt;\n  constexpr static auto fn = bind&lt;H&gt;(G&lt;X&gt;);\n};\n\ntemplate &lt;template &lt;typename&gt; auto G, template &lt;typename&gt; auto H&gt;\nconsteval auto assoc_law(std::meta::info m) {\n  auto lhs = bind&lt;H&gt;(bind&lt;G&gt;(m));\n  auto rhs = bind&lt;assoc_rhs_part&lt;G, H&gt;::template fn&gt;(m);\n  return equal(lhs, rhs);\n}<\/code><\/pre>\n\n\n\n<p>And Robert is your mother&#8217;s brother. <a href=\"https:\/\/compiler-explorer.com\/z\/o4fG3P5K3\">https:\/\/compiler-explorer.com\/z\/o4fG3P5K3<\/a><\/p>\n\n\n\n<p>So this is (one reason) why reflection is powerful: among other things, it implements the type function composition monad. With <code>^^<\/code>, <code>substitute<\/code> and <code>extract&lt;std::meta::info&gt;<\/code> we have everything we need. And once you grok this, you&#8217;re off to the races. Makes sense, right? One intuition for monads is that they lift values and computations into a new world &#8212; in this case, the reflected world.<\/p>\n\n\n\n<p>C++26 gives us type function composition machinery in the language, and TMP libraries everywhere will be able to take advantage.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>&#8220;Discovery consists of seeing what everybody has seen, and thinking what nobody has thought.&#8221; &#8212; Albert Szent-Gy\u00f6rgi, 1937 Nobel Laureate for Medicine I&#8217;m sure others have thought this, but they&#8217;re certainly not saying much about it. Barry&#8217;s recent blog post &#8220;Behold the power of meta::substitute&#8221; got so close to saying&#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-1813","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\/1813","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=1813"}],"version-history":[{"count":11,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1813\/revisions"}],"predecessor-version":[{"id":1829,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1813\/revisions\/1829"}],"wp:attachment":[{"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1813"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1813"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.elbeno.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1813"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}