On Sun, Sep 13, 2020 at 4:37 AM Dvir Yitzchaki via Std-Proposals <std-proposals@lists.isocpp.org> wrote:


On Sun, 13 Sep 2020 at 11:49, Jorg Brown via Std-Proposals <std-proposals@lists.isocpp.org> 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] 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