Date: Sun, 13 Sep 2020 10:30:48 -0500
On Sun, Sep 13, 2020 at 4:37 AM Dvir Yitzchaki via Std-Proposals <
std-proposals_at_[hidden]> wrote:
>
>
> On Sun, 13 Sep 2020 at 11:49, Jorg Brown via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>>
>> OK, so if I only use local control structures, and eschew goto and
>> return, then how do I write the typical multiple-error-return flow, without
>> ending up with code that is increasingly indented, right up until the end?
>> What's the right way, according to your model, of writing:
>>
>> std::optional<std::string> FindUsersCity(bool non_default) {
>> std::optional<std::string> city = std::nullopt;
>> std::optional<ContactsServer> contacts =
>> GetOrOpenContactsServerConnection();
>> if (contacts) {
>> std::optional<UserId> uid = contacts->GetUserId();
>> if (uid) {
>> std::optional<GeoServer> geo =
>> GetOrOpenGeoServerConnection();
>> if (geo) {
>> std::optional<Location> uloc = geo->GetLocation(*uid);
>> if (uloc) {
>> city = uloc->GetCityName();
>> }
>> }
>> }
>> }
>> return city;
>> }
>>
>
> with [P0798] <https://wg21.link/p0798r4> it can be something like:
>
> std::optional<std::string> FindUsersCity(bool non_default) {
> return GetOrOpenContactsServerConnection()
> .transform([](auto&& contacts) { return contacts->GetUserId(); })
> .and_then([](auto&& uid) {
> return GetOrOpenGeoServerConnection().transform(
> [&uid](auto&& geo) { return geo->GetLocation(*uid); });
> })
> .transform([](auto&& uloc) { return uloc->GetCityName(); });
> }
>
Just a correction. Transform takes an optional<T> and a function T->U and
gives you an optional<U>. GetUserId() returns an optional<UserId>, so with
your first transform here we'd end up with an optional<optional<UserId>>.
Also the function takes a T, not an optional<T>, so the body should be
using . not ->. I'm not sure if there should be any uses of transform here
at all, it depends on what Location::GetCityName() actually returns -- from
the code it could be either a string or an optional<string>. Since
everything else returns an optional, I'm assuming that one does too, so
that should also be and_then():
std::optional<std::string> FindUsersCity(bool non_default) {
return GetOrOpenContatsServerConnection()
.and_then([](ContactsServer cs){ return cs.GetUserId(); })
.and_then([](UserId uid){
return GetOrOpenGeoServerConnection()
.and_then([&](GeoServer gs){ return gs.GetLocationId(uid);
})
.and_then([](Location uloc){ return uloc.GetCityName(); });
})
}
If GetCityName() returns just a string, then the last one should be a
transform. Or use coroutines (remains to be seen how well this gets
optimized relative to the previous):
std::optional<std::string> FindUsersCity(bool non_default) {
ConstactsSeriver cs = co_await GetOrOpenContatsServerConnection();
UserId uid = co_await cs.GetUserId();
GeoServer gs = co_await GetOrOpenGeoServerConnection();
Location uloc = co_await gw.GetLocationId(uid);
return uloc.GetCityName();
}
Barry
std-proposals_at_[hidden]> wrote:
>
>
> On Sun, 13 Sep 2020 at 11:49, Jorg Brown via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>>
>> OK, so if I only use local control structures, and eschew goto and
>> return, then how do I write the typical multiple-error-return flow, without
>> ending up with code that is increasingly indented, right up until the end?
>> What's the right way, according to your model, of writing:
>>
>> std::optional<std::string> FindUsersCity(bool non_default) {
>> std::optional<std::string> city = std::nullopt;
>> std::optional<ContactsServer> contacts =
>> GetOrOpenContactsServerConnection();
>> if (contacts) {
>> std::optional<UserId> uid = contacts->GetUserId();
>> if (uid) {
>> std::optional<GeoServer> geo =
>> GetOrOpenGeoServerConnection();
>> if (geo) {
>> std::optional<Location> uloc = geo->GetLocation(*uid);
>> if (uloc) {
>> city = uloc->GetCityName();
>> }
>> }
>> }
>> }
>> return city;
>> }
>>
>
> with [P0798] <https://wg21.link/p0798r4> it can be something like:
>
> std::optional<std::string> FindUsersCity(bool non_default) {
> return GetOrOpenContactsServerConnection()
> .transform([](auto&& contacts) { return contacts->GetUserId(); })
> .and_then([](auto&& uid) {
> return GetOrOpenGeoServerConnection().transform(
> [&uid](auto&& geo) { return geo->GetLocation(*uid); });
> })
> .transform([](auto&& uloc) { return uloc->GetCityName(); });
> }
>
Just a correction. Transform takes an optional<T> and a function T->U and
gives you an optional<U>. GetUserId() returns an optional<UserId>, so with
your first transform here we'd end up with an optional<optional<UserId>>.
Also the function takes a T, not an optional<T>, so the body should be
using . not ->. I'm not sure if there should be any uses of transform here
at all, it depends on what Location::GetCityName() actually returns -- from
the code it could be either a string or an optional<string>. Since
everything else returns an optional, I'm assuming that one does too, so
that should also be and_then():
std::optional<std::string> FindUsersCity(bool non_default) {
return GetOrOpenContatsServerConnection()
.and_then([](ContactsServer cs){ return cs.GetUserId(); })
.and_then([](UserId uid){
return GetOrOpenGeoServerConnection()
.and_then([&](GeoServer gs){ return gs.GetLocationId(uid);
})
.and_then([](Location uloc){ return uloc.GetCityName(); });
})
}
If GetCityName() returns just a string, then the last one should be a
transform. Or use coroutines (remains to be seen how well this gets
optimized relative to the previous):
std::optional<std::string> FindUsersCity(bool non_default) {
ConstactsSeriver cs = co_await GetOrOpenContatsServerConnection();
UserId uid = co_await cs.GetUserId();
GeoServer gs = co_await GetOrOpenGeoServerConnection();
Location uloc = co_await gw.GetLocationId(uid);
return uloc.GetCityName();
}
Barry
Received on 2020-09-13 10:31:02