Topic: Idea: New Language Feature for Safer, Easier Optional Access


Author: David Peterson <djpeterson83@hotmail.com>
Date: Sat, 22 Apr 2017 14:23:52 -0700 (PDT)
Raw View
------=_Part_1129_1663349901.1492896232193
Content-Type: multipart/alternative;
 boundary="----=_Part_1130_1259922149.1492896232193"

------=_Part_1130_1259922149.1492896232193
Content-Type: text/plain; charset=UTF-8

Hi Everyone, this is my first time floating an idea so apologies if I get
the format wrong.  Constructive criticism is very welcome!

I've had a look at the new std::optional<T> and as exciting as it is to
finally have such a useful type in the standard, I worry that the feature
may be too cumbersome and possibly too easy to use incorrectly.  Here's an
example of its current usage as provided by the C++ 17 standard:

// C++ 17 New Optional Usage //
if (auto customer = get_customer(customer_id)) {
    process(*customer);
}
else {
    show_customer_not_found();
}

This isn't too bad, but I think it could be better.  The first issue I have
is with `customer` being an actual std::optional instance.  This creates a
level of indirection that seems awkward to use.  One must also remember to
dereference the `customer` optional when attempting to gain access to the
value as can be seen during the `process` call.  It would be nice to remove
this indirection and gain direct access to the underlying type rather than
through the optional itself.  After some thinking, I realized that the
range-based for loop had the solution I was looking for.  Essentially, an
optional is a list containing either no items or exactly one item.  In such
a case, one could think of iterating over an optional as if it were a
simple container:

// Example of an optional as if it were a container of one or zero items //
for (auto& customer : get_customer(customer_id)) {
    process(customer);
}

Obviously, this syntax is a bit awkward.  It just doesn't make sense to
consider an optional as a container.  Furthermore, there would likely be a
high degree of unnecessary overhead in interpreting the optional as a
container.  Iterators would also have to be made to support the begin/end
range.  To correct these shortcomings, my proposal is to extend the
range-based for loop syntax for single instance item which may or may not
exist (optionals and pointers, primarily) to the `if` statement.  Here is
an example:

// New way with `if` auto-dereferencing language feature //
if (auto& customer : get_customer(customer_id)) {
    process(customer); // customer is a Customer& and not an optional.
                       // We can safely use customer as we are guaranteed
                       // to have a valid reference and that reference
                       // cannot extend beyond the `if` scope
}
else {
    show_customer_not_found();
}

As you can see, this feature is readable and addresses the indirection
problem from earlier.  Furthermore, it can be optimized easier for optional
types and can theoretically work with any type, including user types, so
long as they provide a conversion to boolean operator and a dereference
operator.  An example of how this could be interpreted follows:

// New feature translates into... //
auto _optional_customer = get_customer(customer_id);
if (_optional_customer) {
    auto& customer = *_optional_customer;
    process(customer);
}
else {
    show_customer_not_found();
}

I'm sure language features are not the easiest thing to add, and I'm no
compiler developer, but I'm anxious to hear any feedback you may have in
regards to short comings, enhancements and overall complexity to implement.
 Thank you for considering this feature. - David Peterson

--
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.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/bacbf6b9-06a8-4f30-b8f0-74a85a875191%40isocpp.org.

------=_Part_1130_1259922149.1492896232193
Content-Type: text/html; charset=UTF-8
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr">Hi Everyone, this is my first time floating an idea so apo=
logies if I get the format wrong. =C2=A0Constructive criticism is very welc=
ome!<div><br></div><div>I&#39;ve had a look at the new std::optional&lt;T&g=
t; and as exciting as it is to finally have such a useful type in the stand=
ard, I worry that the feature may be too cumbersome and possibly too easy t=
o use incorrectly. =C2=A0Here&#39;s an example of its current usage as prov=
ided by the C++ 17 standard:</div><div><br></div><div><div style=3D"color: =
rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Consola=
s, &quot;Courier New&quot;, monospace; font-size: 14px; line-height: 19px;"=
><div><span style=3D"color: #608b4e;">//=C2=A0C++=C2=A017=C2=A0New=C2=A0Opt=
ional Usage //</span></div><div><span style=3D"color: #c586c0;">if</span>=
=C2=A0(<span style=3D"color: #569cd6;">auto</span>=C2=A0customer=C2=A0=3D=
=C2=A0get_customer(customer_id))=C2=A0{</div><div>=C2=A0=C2=A0=C2=A0=C2=A0<=
span style=3D"color: #dcdcaa;">process</span>(*customer);</div><div>}</div>=
<div><span style=3D"color: #c586c0;">else</span>=C2=A0{</div><div>=C2=A0=C2=
=A0=C2=A0=C2=A0<span style=3D"color: #dcdcaa;">show_customer_not_found</spa=
n>();</div><div>}</div></div></div><div><br></div><div>This isn&#39;t too b=
ad, but I think it could be better. =C2=A0The first issue I have is with `c=
ustomer` being an actual std::optional instance. =C2=A0This creates a level=
 of indirection that seems awkward to use. =C2=A0One must also remember to =
dereference the `customer` optional when attempting to gain access to the v=
alue as can be seen during the `process` call. =C2=A0It would be nice to re=
move this indirection and gain direct access to the underlying type rather =
than through the optional itself. =C2=A0After some thinking, I realized tha=
t the range-based for loop had the solution I was looking for. =C2=A0Essent=
ially, an optional is a list containing either no items or exactly one item=
.. =C2=A0In such a case, one could think of iterating over an optional as if=
 it were a simple container:</div><div><br></div><div><div style=3D"color: =
rgb(212, 212, 212); background-color: rgb(30, 30, 30); font-family: Consola=
s, &quot;Courier New&quot;, monospace; font-size: 14px; line-height: 19px;"=
><div><span style=3D"color: #608b4e;">//=C2=A0Example=C2=A0of=C2=A0an=C2=A0=
optional=C2=A0as=C2=A0if=C2=A0it=C2=A0were=C2=A0a=C2=A0container=C2=A0of=C2=
=A0one=C2=A0or=C2=A0zero=C2=A0items=C2=A0//</span></div><div><span style=3D=
"color: #c586c0;">for</span>=C2=A0(<span style=3D"color: #569cd6;">auto</sp=
an>&amp;=C2=A0customer=C2=A0:=C2=A0get_customer(customer_id))=C2=A0{</div><=
div>=C2=A0=C2=A0=C2=A0=C2=A0<span style=3D"color: #dcdcaa;">process</span>(=
customer);</div><div>}</div></div></div><div><br></div><div>Obviously, this=
 syntax is a bit awkward. =C2=A0It just doesn&#39;t make sense to consider =
an optional as a container. =C2=A0Furthermore, there would likely be a high=
 degree of unnecessary overhead in interpreting the optional as a container=
.. =C2=A0Iterators would also have to be made to support the begin/end range=
.. =C2=A0To correct these shortcomings, my proposal is to extend the range-b=
ased for loop syntax for single instance item which may or may not exist (o=
ptionals and pointers, primarily) to the `if` statement. =C2=A0Here is an e=
xample:</div><div><br></div><div><div style=3D"color: rgb(212, 212, 212); b=
ackground-color: rgb(30, 30, 30); font-family: Consolas, &quot;Courier New&=
quot;, monospace; font-size: 14px; line-height: 19px;"><div><div style=3D"l=
ine-height: 19px;"><div><span style=3D"color: #608b4e;">//=C2=A0New=C2=A0wa=
y=C2=A0with=C2=A0`if` auto-dereferencing language=C2=A0feature=C2=A0//</spa=
n></div><div><span style=3D"color: #c586c0;">if</span>=C2=A0(<span style=3D=
"color: #569cd6;">auto</span>&amp;=C2=A0customer=C2=A0:=C2=A0get_customer(c=
ustomer_id))=C2=A0{</div><div>=C2=A0=C2=A0=C2=A0=C2=A0<span style=3D"color:=
 #dcdcaa;">process</span>(customer);=C2=A0<span style=3D"color: #608b4e;">/=
/=C2=A0customer=C2=A0is=C2=A0a=C2=A0Customer&amp;=C2=A0and=C2=A0not=C2=A0an=
=C2=A0optional.</span></div><div>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0<span style=3D"color: #608b4e;">//=C2=A0We=C2=A0can=C2=
=A0safely=C2=A0use=C2=A0customer=C2=A0as=C2=A0we=C2=A0are=C2=A0guaranteed</=
span></div><div>=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=
=C2=A0<span style=3D"color: #608b4e;">//=C2=A0to=C2=A0have=C2=A0a=C2=A0vali=
d=C2=A0reference=C2=A0and=C2=A0that=C2=A0reference</span></div><div>=C2=A0=
=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=
=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0<span style=3D"col=
or: #608b4e;">//=C2=A0cannot=C2=A0extend=C2=A0beyond=C2=A0the `if` scope</s=
pan></div><div>}</div><div><span style=3D"color: #c586c0;">else</span>=C2=
=A0{</div><div>=C2=A0=C2=A0=C2=A0=C2=A0<span style=3D"color: #dcdcaa;">show=
_customer_not_found</span>();</div><div>}</div></div></div></div></div><div=
><br></div><div>As you can see, this feature is readable and addresses the =
indirection problem from earlier. =C2=A0Furthermore, it can be optimized ea=
sier for optional types and can theoretically work with any type, including=
 user types, so long as they provide a conversion to boolean operator and a=
 dereference operator. =C2=A0An example of how this could be interpreted fo=
llows:</div><div><br></div><div><div style=3D"color: rgb(212, 212, 212); ba=
ckground-color: rgb(30, 30, 30); font-family: Consolas, &quot;Courier New&q=
uot;, monospace; font-size: 14px; line-height: 19px;"><div><span style=3D"c=
olor: #608b4e;">//=C2=A0New=C2=A0feature=C2=A0translates=C2=A0into...=C2=A0=
//</span></div><div><span style=3D"color: #569cd6;">auto</span>=C2=A0_optio=
nal_customer=C2=A0=3D=C2=A0get_customer(customer_id);</div><div><span style=
=3D"color: #c586c0;">if</span>=C2=A0(_optional_customer)=C2=A0{</div><div>=
=C2=A0=C2=A0=C2=A0=C2=A0<span style=3D"color: #569cd6;">auto</span>&amp;=C2=
=A0customer=C2=A0=3D=C2=A0*_optional_customer;</div><div>=C2=A0=C2=A0=C2=A0=
=C2=A0<span style=3D"color: #dcdcaa;">process</span>(customer);</div><div>}=
</div><div><span style=3D"color: #c586c0;">else</span>=C2=A0{</div><div>=C2=
=A0=C2=A0=C2=A0=C2=A0<span style=3D"color: #dcdcaa;">show_customer_not_foun=
d</span>();</div><div>}</div></div></div><div><br></div><div>I&#39;m sure l=
anguage features are not the easiest thing to add, and I&#39;m no compiler =
developer, but I&#39;m anxious to hear any feedback you may have in regards=
 to short comings, enhancements and overall complexity to implement. =C2=A0=
Thank you for considering this feature. - David Peterson</div></div>

<p></p>

-- <br />
You received this message because you are subscribed to the Google Groups &=
quot;ISO C++ Standard - Future Proposals&quot; group.<br />
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to <a href=3D"mailto:std-proposals+unsubscribe@isocpp.org">std-proposa=
ls+unsubscribe@isocpp.org</a>.<br />
To post to this group, send email to <a href=3D"mailto:std-proposals@isocpp=
..org">std-proposals@isocpp.org</a>.<br />
To view this discussion on the web visit <a href=3D"https://groups.google.c=
om/a/isocpp.org/d/msgid/std-proposals/bacbf6b9-06a8-4f30-b8f0-74a85a875191%=
40isocpp.org?utm_medium=3Demail&utm_source=3Dfooter">https://groups.google.=
com/a/isocpp.org/d/msgid/std-proposals/bacbf6b9-06a8-4f30-b8f0-74a85a875191=
%40isocpp.org</a>.<br />

------=_Part_1130_1259922149.1492896232193--

------=_Part_1129_1663349901.1492896232193--

.