Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a a predefined way to skip calculations that result in Nothing?

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?

like image 868
bwroga Avatar asked Oct 27 '16 15:10

bwroga


2 Answers

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-Nothings), 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)
like image 93
chi Avatar answered Oct 05 '22 19:10

chi


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.

like image 38
Zeta Avatar answered Oct 05 '22 18:10

Zeta