Stas Shepelev
Stas Shepelev

Reputation: 165

Parallel functions composition in Functional Programming

This question is a sub-problem of a bigger problem and this is the last hard nut:

I have two functions:

getUserFromDB :: Token -> Either String User
getNewUser :: Token -> Either String User

How do I combine them to get a function like this:

getUser :: Token -> Either String User

so that getUserFromDB and getNewUser are tried in sequence one after another, are fed the same input (Token is an api token for OAuth api) and the result of the first succeeded function is returned, or some default value if none succeed?

In other words: if there is a function in FP with signature like:

tryUntilGoodOrFail :: failValue -> (result -> Boolean) -> [fn] -> input -> result
OR
tryUntilGoodOrFail :: a -> (a -> Boolean) -> [(b -> a)] -> b -> a

EDIT

This is just a newbie question about functional programming in general. I just don't know how to put two (or more) functions in parallel sequence, in contrast to consecutive functions pipe (which FP is very good at). But in realworld cases this parallel sequence functions pattern is what often needed. I believe FP has it's own solution to this. I just can't find it anywhere.

Upvotes: 0

Views: 227

Answers (3)

Stas Shepelev
Stas Shepelev

Reputation: 165

The simplest solution I have found (working for any language lazy or not): fold (foldl to be more accurate). Don't know how to express it in Haskell or PureScript. Here is my version in Python:

getUser = lambda token: fold(
  lambda e,f:e if e.isRight else f(token),
  left('None Worked'),
  [getUserFromDB, getNewUser, someOtherAttempt, ... ]
)

Same for JS:

const getUser = token => fold(
  (e,f)=>e.isRight?e:f(token),
  left('None Worked'),
  [getUserFromDB, getNewUser, someOtherAttempt, ... ]
)

PureScript has interesting function called until that also can help with it. But it returns a list of results until the first successful one (which is what I was looking for), and I can feed it to last and maybe and get a first good result or a default value. But fold makes it all in one go and is more general and universal and standard.

Upvotes: 0

melston
melston

Reputation: 2340

This kind of sounds like Applicative Functors (also here). Assuming getNewUser will return a Left (or Failure depending on the language) if the user is already in the system then you can use a list of functions and just apply them in sequence and get a list of Result back. Then you can find the first successful result without caring whether it is a new user or an existing one.

This doesn't short-circuit but it does the next best thing.

Upvotes: 0

Karl Bielefeldt
Karl Bielefeldt

Reputation: 49078

Part of the problem is Eithers are right-biased, meaning they are designed to short-circuit on the first Left, but you want it to short-circuit on the first Right. If you made everything an Either User String, then your getUser would be:

getUser token = getUserFromDb token >> getNewUser token

If you want to keep it as Either String User, you have to swap it then bind, or do the short-circuiting yourself using an if expression or something.

Upvotes: 1

Related Questions