<div dir="ltr"><div class="gmail_quote gmail_quote_container"><div dir="ltr" class="gmail_attr">On Sun, Mar 30, 2025 at 10:41 PM Stewart Becker via Std-Proposals &lt;<a href="mailto:std-proposals@lists.isocpp.org">std-proposals@lists.isocpp.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><u></u>

  

    
  
  <div>
    <p>I have been doing work in the world of monads and functional
      programming, and have found std::expected to be a key building
      block for such work.  It works particularly well when the error
      type is the catch-all std::exception_ptr.  However, there are
      still some drawbacks.  What would be even better would be if:</p>
    <ol>
      <li>std::exception_ptr supported references (including rvalue
        references) for the value type,</li>
      <li>std::exception_ptr::value() called
        std::rethrow_exception(error()) rather than throwing a
        std::bad_expected_access on error, and</li>
      <li>std::exception_ptr&lt;T, std::exception_ptr&gt;::transform(f)
        and ::and_then(f) were noexcept.  Completely noexcept is
        probably impossible, but it should be possible to wrap the
        transformation itself in try/catch, only throwing if the final
        copy/move of the returned result does so. <br></li></ol></div></blockquote><div>I believe you meant `std::expected` instead of std::exception_ptr in the above points? (Only the second template parameter in the third point should be std::exception_ptr). <br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div><ol><li>
      </li>
    </ol>
    <p>To the first point, I have found that, in order to work
      effectively with monadic continuations, more than return-by-value
      is needed.  While a reference can be wrapped in a std::reference_wrapper
      or a pointer, this doesn&#39;t allow for generic programming.  There
      also are no vocabulary wrappers that distinguish between lvalue
      and rvalue references.<br>
    </p>
    <p>The paper P2988 (std::optional&lt;T&amp;&gt;)
      expected&lt;T&amp;&gt; in passing and makes the case for
      std::expected&lt;T&amp;, E&gt;, but only actually proposes matters
      for std::optional&lt;T&amp;&gt;, noting &quot;... we expect future
      papers to propose std::expected&lt;T&amp;, E&gt; ...&quot;.  Well,
      maybe it is time for such a paper.
    </p>
    <p>Secondly, the purpose of std::expected is to encpasulate error
      handling, but when exp.transform(f) and exp.and_then(f) throw,
      this encapsulation is broken.  Additionally, I find that throwing
      std::bad_expected_access is a leaky abstraction - albeit one
      necessary for other error types.  In each case, it can be worked
      around by wrapping calls to the expected in a try / catch and
      either rewrapping (.transform) or extracting and rethrowing
      (.value) the caught exception.  However, this adds a lot of
      boilerplate and significantly reduces the advantages of using
      std::extepected in the first place!  This could all be covered
      within std::expected&lt;T, std::exception_ptr&gt; itself.<br>
    </p>
    <p>Paper P3014 (Customising std::expected&#39;s exception) touches on
      some of the above, but I don&#39;t think in its current form would
      accommodate calling rethrow_exception when E is
      std::exception_ptr.  While P3014 states that &quot;<span style="color:rgb(0,0,0);font-size:medium;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:justify;text-indent:0px;text-transform:none;word-spacing:0px;white-space:normal;text-decoration-style:initial;text-decoration-color:initial;display:inline;float:none">User
        code is allowed to customize<span> std::expected_traits</span></span><span style="color:rgb(0,0,0);font-size:medium;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;text-align:justify;text-indent:0px;text-transform:none;word-spacing:0px;white-space:normal;text-decoration-style:initial;text-decoration-color:initial;display:inline;float:none">
        for their own error types&quot;, </span>std::exception_ptr is not a
      user type.</p>
    <p>I do acknowledge that for std::expected&lt;T,
      std::exception_ptr&gt;::value to call std::rethrow_exception would
      be a change of behaviour.  However, one way to square this circle
      could be if std::expected took a third, defaulted, type parameter,
      taking the role of P3014&#39;s expected_traits&lt;E&gt;.  Current
      behaviour can be retained by the default but still allow for
      customisation.  I don&#39;t know if there is precedent for adding a
      type argument to an existing library class template.  While this
      too would be a breaking change inasmuch as adding an addition
      template paremeter means std::expected would no longer match
      template&lt;template&lt;typename, typename&gt; class Exp&gt;, it
      is perhaps the lesser of two evils.  Beyond that, it is difficult
      to see how this would break existing code.  I have reached out to
      Jonathan Muller (P3014&#39;s author), but received no response. <br>
    </p>
    <p>I don&#39;t know of any work on noexcept versions of
      std::expected::transform or std::expected::and_then.  These are
      perhaps the simplest to achieve.  std::expected&lt;T,
      std::exception_ptr&gt; could be specialised to have additional
      noexcept member functions try_transform and try_and_then, which
      internally call transform(f) and and_then(f) within a try/catch
      block.<br>
    </p>
    <p>I would hope that adding support for reference types to
      std::expected is uncontroversial, particularly in the light of
      P2988.  The other aspects may be more contentious.  It would be
      possible to design an entirely new std::expected-like class
      template with those features.  However, std::expected delivers a
      lot of valuable functionality that would simply be duplicated; do
      the additional features above really warrant a new type rather
      than attempting to improve std::expected?  We already have at
      least three patterns for indicating errors, (returning error
      codes, throwing exceptions and encapsulating in std::expected). 
      Adding yet another - especially one so similar to an existing
      pattern - would seem to complicate the space for little benefit. 
      On the other hand, if the type could implicitly convert to and
      from std::expected, it may be a viable way forward.</p>
    <p>Stewart Becker<br>
    </p>
    <p><br>
    </p>
    <p><br>
    </p>
    <p></p>
  </div>

-- <br>
Std-Proposals mailing list<br>
<a href="mailto:Std-Proposals@lists.isocpp.org" target="_blank">Std-Proposals@lists.isocpp.org</a><br>
<a href="https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals" rel="noreferrer" target="_blank">https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals</a><br>
</blockquote></div></div>

