DEV Community

Discussion on: Ask me dumb questions about functional programming

Collapse
 
shepelevstas profile image
Shepelev Stas

I have two functions:
getUserFromDB :: Token -> Either NotFound User
and
getNewUser :: Token -> Either NoMoreInfo User
The Token is a token from OAuth api response. getNewUser uses it to get more info from api and create new user in my DB. and getUserFromDB just gets a user from DB with the help of token info. The problem is: how do I combine these two functions into one like getUser :: Token -> Either OneOfTwoPossibleErrors User, which will first try to getUserFromDB feeding token to it and then, if it fails, try to getNewUser again feeding the same token to it? FP is very good at creating consecutive streams that process data, but how to create two or more parallel pipes that are tried in sequence until one of them returns good result, and return any result from the last one if none succeeds?

Collapse
 
joelnet profile image
JavaScript Joel

It depends on the library you are using for the Either. But maybe these docs from Folktale's Either can help: folktale.origamitower.com/docs/v2....

There's a leftMap method that will run when the either is a Left. So it would look like

getUserFromDb(token)
  .leftMap(err => getNewUser(token))
  .cata({
    Left:  err => {},
    Right: value => {}
  });

Collapse
 
shepelevstas profile image
Shepelev Stas

Thanks! Good to know. But what about more general way, for example if I had a list of functions to try in order, like [getUserFromDb, getNewUser, trySmthElse, ....]? And I don't want to map that list into a list of results because that would mean execution of all of them, and I don't need a new user if getUserFromDB had found the one I needed.

Thread Thread
 
joelnet profile image
JavaScript Joel

Maybe something like this?

f = pipe([
  leftMap(getUserFromDb),
  leftMap(getNewUser),
  leftMap(trySmthElse)
])
Thread Thread
 
shepelevstas profile image
Shepelev Stas

Isn't leftMap, as a function (not a method .leftMap) takes an Either as its first argument? And to put up a pipe of leftMaps with Eithers I would need to first get all the Eithers and that would mean running all the functions again, which I try to avoid.

Thread Thread
 
shepelevstas profile image
Shepelev Stas • Edited

There is a function until in PureScript that is the closest to what I'm looking for: until :: ( a -> Boolean ) -> m a -> m a, but m is Monad, and I don't quite get yet how I can put a list of functions in it so that they will all get the same input value and be run one by one and tested after each run. until then returns a m a that is also can just be a list of results, kind of like map would produce, but that list will end as soon as (a -> Boolean) evaluates to true. Then I could just last on that m a result and, according to last definition, -> maybe get a successful output from the first succeeded function in the list =)

Thread Thread
 
shepelevstas profile image
Shepelev Stas

I think I found a somewhat simple solution!

const getUser = token => fold(
  (res, f) => res.isRight ? res : f(token),
  left('None Worked'),
  [getUserFromDB, getNewUser, someOtherAttempt, ...]
)
Thread Thread
 
joelnet profile image
JavaScript Joel

Ahh okay. I see what you are trying to do. Nice solution!