首页 文章

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

提问于
浏览
17

我决定今天是我修复一些不必要地在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 回答

  • 29

    让我们退一步吧 . 您有两种类型的函数,一些是纯函数,类型为 a -> b ,另一些是 a -> m b 类型的monadic .

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

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

    • Pure then pure. 使用常规函数组合 (.) .
    g :: a -> b
     f :: b -> c
     f . g :: a -> c
    
    • Pure then monadic . 也可以使用 (.) .
    g :: a -> b
     f :: b -> m c
     f . g :: a -> m c
    
    • Monadic then monadic . 使用kleisli成分 (<=<) .
    g :: a -> m b
     f :: b -> m c
     f <=< g :: a -> m c
    
    • Monadic then pure . 在纯函数上使用 fmap 并使用 (.) 撰写 .
    g :: a -> m b
     f :: b -> c
     fmap f . g :: a -> m c
    

    忽略所涉及类型的细节,您的功能是:

    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
    

    我们已经完成了 .

  • 5

    要填写FUZxxl 's answer, let' s pureify flagWeekEnds

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

    你经常在变量名( day - > days )之后放一个"s"列表(就像你用英语复数一样) .

  • 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
    

相关问题