I have a these three functions
a :: Int -> Maybe Int
a i = if i < 100 then Just i else Nothing
b :: Int -> Maybe Int
b i = if i < 50 then Just i else Nothing
c :: Int -> Maybe Int
c i = if i > 0 then Just i else Nothing
And I want to chain them together so that when the result of one function results in a Nothing
the input of that function is returned instead.
I can achieve this with this function:
import Data.Maybe (fromMaybe)
e :: Int -> [Int -> Maybe Int] -> Int
e i [] = i
e i (f:fs) = e (fromMaybe i $ f i) fs
-
*Main> e 75 [a,b,c]
75
Is there an existing function, Monad instance, or other way in the base libraries that exhibits this behavior?
Expanding my comment above -- this approach is not too different from the code the OP posted.
We first define how to turn a function a -> Maybe a
into a -> a
, substituting the input for Nothing
.
totalize :: (a -> Maybe a) -> (a -> a)
totalize f x = fromMaybe x (f x)
Then, we exploit the above: we make every function "total" (meaning no-Nothing
s), wrap it as an Endo
, then we compose the list of endomorphisms (mconcat
is composition in the Endo
monoid).
e :: [a -> Maybe a] -> a -> a
e = appEndo . mconcat . map (Endo . totalize)
or even (as suggested below)
e :: Foldable t => t (a -> Maybe a) -> a -> a
e = appEndo . foldMap (Endo . totalize)
Well, you can create a a -> a
from a a -> Maybe a
:
repair :: (a -> Maybe a) -> a -> a
repair f x = fromMaybe x (f x)
Afterwards, you can just combine (.)
and repair
:
andThen :: (a -> Maybe a) -> (a -> Maybe a) -> a -> a
andThen f g = repair g . repair f
But there's no library function for that, since there is no general way to get a value out of a Monad
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With