Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I organize my pure functions with my monadic actions idiomatically

I've decided today is the day I fix some of my pure functions that are unnecessarily running in a monadic action. Here's what I have.

flagWorkDays :: [C.Day] -> Handler [WorkDay] 
flagWorkDays dayList =
   flagWeekEnds dayList >>=
   flagHolidays >>=
   flagScheduled >>=
   flagASAP >>=
   toWorkDays

Here is flagWeekEnds, as of now.

flagWeekEnds :: [C.Day] -> Handler [(C.Day,Availability)]
flagWeekEnds dayList = do
   let yepNope = Prelude.map isWorkDay dayList
       availability = Prelude.map flagAvailability yepNope
   return $ Prelude.zip dayList availability

flagHolidays follows a similar pattern. toWorkDays just changes one type to another, and is a pure function.

flagScheduled, and flagASAP are monadic actions. I am not sure how to combine the monadic actions with the pure functions idiomatically in flagWorkDays. Could someone help me fix flagWorkDays, assuming flagWeekEnds and flagHolidays have been made pure?

like image 410
Michael Litchard Avatar asked Nov 22 '11 21:11

Michael Litchard


1 Answers

Let's take a step back for a moment. You have two types of functions, some pure with types of the form a -> b, and some monadic of type a -> m b.

To avoid confusion, let's also stick with right-to-left composition. If you prefer to read left-to-right, just reverse the order of the functions and replace (<=<) with (>=>), and (.) with (>>>) from Control.Arrow.

There are then four possibilities for how these can be composed.

  1. Pure then pure. Use regular function composition (.).

     g :: a -> b
     f :: b -> c
     f . g :: a -> c
    
  2. Pure then monadic. Also use (.).

     g :: a -> b
     f :: b -> m c
     f . g :: a -> m c
    
  3. Monadic then monadic. Use kleisli composition (<=<).

     g :: a -> m b
     f :: b -> m c
     f <=< g :: a -> m c
    
  4. Monadic then pure. Use fmap on the pure function and (.) to compose.

     g :: a -> m b
     f :: b -> c
     fmap f . g :: a -> m c
    

Ignoring the specifics of the types involved, your functions are:

flagWeekEnds :: a -> b
flagHolidays :: b -> c
flagScheduled :: c -> m d
flagASAP :: d -> m e
toWorkDays :: e -> f

Let's go from the top. flagWeekEnds and flagHolidays are both pure. Case 1.

flagHolidays . flagWeekEnds
  :: a -> c

This is pure. Next up is flagScheduled, which is monadic. Case 2.

flagScheduled . flagHolidays . flagWeekEnds
  :: a -> m d

Next is flagASAP, now we have two monadic functions. Case 3.

flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds
  :: a -> m e

And finally, we have the pure function toWorkDays. Case 4.

fmap toWorkDays . flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds
  :: a -> m f

And we're done.

like image 168
hammar Avatar answered Sep 28 '22 09:09

hammar