如何用惯用的monadic动作组织我的纯函数

时间:2011-11-22 21:37:53

标签: haskell monads function-composition

我今天决定在我修复一些不必要的monadic动作的纯函数的那一天。这就是我所拥有的。

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

截至目前,这是flagWeekEnds。

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遵循类似的模式。 toWorkDays只是将一种类型更改为另一种类型,并且是纯函数。

flagScheduledflagASAP是monadic行动。我不确定如何在flagWorkDays中以惯用方式将monadic动作与纯函数结合起来。有人可以帮我修复flagWorkDays,假设flagWeekEndsflagHolidays是纯净的吗?

3 个答案:

答案 0 :(得分:29)

让我们退一步。您有两种类型的函数,一些是纯函数,其格式为a -> b,另一些函数为a -> m b

为避免混淆,让我们坚持从右到左的构图。如果您希望从左向右阅读,只需颠倒函数的顺序,将(<=<)替换为(>=>),将(.)替换为(>>>) Control.Arrow

然后有四种可能性来组成这些。

  1. 纯净然后纯粹。使用常规函数组合(.)

     g :: a -> b
     f :: b -> c
     f . g :: a -> c
    
  2. 纯粹的monadic 。也可以使用(.)

     g :: a -> b
     f :: b -> m c
     f . g :: a -> m c
    
  3. Monadic然后是monadic 。使用kleisli撰写(<=<)

     g :: a -> m b
     f :: b -> m c
     f <=< g :: a -> m c
    
  4. Monadic然后纯粹。在纯函数上使用fmap并使用(.)撰写。

     g :: a -> m b
     f :: b -> c
     fmap f . g :: a -> m c
    
  5. 忽略所涉及类型的细节,您的功能是:

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

    让我们从顶部开始。 flagWeekEndsflagHolidays都是纯粹的。案例1。

    flagHolidays . flagWeekEnds
      :: a -> c
    

    这很纯粹。接下来是flagScheduled,这是monadic。案例2。

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

    接下来是flagASAP,现在我们有两个monadic函数。案例3。

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

    最后,我们有了纯函数toWorkDays。案例4。

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

    我们已经完成了。

答案 1 :(得分:5)

这不是很困难。您基本上只需将(>>=)替换为(.)并翻转操作数顺序。 do语法可能有助于澄清。我还使用Kleisli组合子(鱼)(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c制作了示例pointfree,对于monad来说基本上是(.)

import Control.Monad

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

答案 2 :(得分:5)

要填写FUZxxl的答案,让我们纯化flagWeekEnds

flagWeekEnds :: [C.Day] -> [(C.Day,Availability)]
flagWeekEnds days = days `zip` map (flagAvailability . isWorkDay) days

您经常在变量名称(day - &gt; days)之后添加一个“s”(如同您使用英语中的复数形式)。