|
|
|
|
|
by shortrounddev2
231 days ago
|
|
It also invites exceptions as error handling instead of a monadic (result) pattern. I usually do something more like Result<Users> userRes = getExpiredUsers(db);
if(isError(userRes)) {
return userRes.error;
}
/* This probably wouldn't actually need to return a Result IRL */
Result<Email> emailRes = generateExpireyEmails(userRes.value);
if(isError(emailRes)) {
return emailRes.error;
}
Result<SendResult> sendRes = sendEmails(emailRes.value);
if(isError(sendRes)) {
return sendRes.error;
}
return sendRes; // successful value, or just return a Unit type.
This is in my "functional C++" style, but you can write pipe helpers which sort of do the same thing: Result<SendResult> result = pipe(getExpiredUsers(db))
.then(generateExpireyEmails)
.then(sendEmails)
.result();
if(isError(result)) {
return result.error;
}
If an error result is returned by any of the functions, it terminates immediately and returns the error there. You can write this in most languages, even imperative/oop languages. In java, they have a built in class called Optional with options to treat null returns as empty: Optional.ofNullable(getExpiredUsers(db))
.map(EmailService::generateExpireyEmails)
.map(EmailService::sendEmails)
.orElse(null);
or something close to that, I haven't used java in a couple years.C++ also added a std::expected type in C++23: auto result = some_expected()
.and_then(another_expected)
.and_then(third_expected)
.transform(/* ... some function here, I'm not familiar with the syntax*/);
|
|