17

我决定今天是我修复一些在单子动作中不必要地运行的纯函数的日子。这就是我所拥有的。

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只是将一种类型更改为另一种类型,并且是一个纯函数。

flagScheduled,并且flagASAP是一元动作。我不确定如何将 monadic 动作与flagWorkDays. 有人可以帮我修复flagWorkDays,假设flagWeekEnds并且flagHolidays已经变得纯净了吗?

4

3 回答 3

29

让我们退后一步。您有两种类型的函数,一些具有 form 类型的纯函数,a -> b以及一些类型为 monadic的函数a -> m b

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

那么,如何组合它们有四种可能性。

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

     g :: a -> b
     f :: b -> c
     f . g :: a -> c
    
  2. 纯则一元。也使用(.).

     g :: a -> b
     f :: b -> m c
     f . g :: a -> m c
    
  3. 一元然后一元。使用kleisli 成分(<=<)

     g :: a -> m b
     f :: b -> m c
     f <=< g :: a -> m c
    
  4. 单子则纯。用于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

让我们从顶部开始。flagWeekEnds并且flagHolidays都是纯洁的。情况1。

flagHolidays . flagWeekEnds
  :: a -> c

这是纯粹的。接下来是flagScheduled,它是一元的。案例 2。

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

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

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

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

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

我们完成了。

于 2011-11-22T22:41:00.313 回答
5

这不是很困难。您基本上只是替换(>>=)(.)翻转操作数顺序。do语法可能有助于澄清。我还使用 Kleisli 组合器 (fish) 使示例变得无意义(<=<) :: (b -> m c) -> (a -> m b) -> a -> m c,这基本上是(.)针对 monads 的。

import Control.Monad

flagWorkDays :: [C.Day] -> Handler [WorkDay] 
flagWorkDays =
  fmap toWorkDays . flagASAP <=< flagScheduled . flagHolidays . flagWeekEnds
于 2011-11-22T21:53:57.020 回答
5

要填写 FUZxxl 的答案,让我们进行 pureify flagWeekEnds

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

day当它是一个列表时,你经常在变量名 ( -> )后面加上一个“s” days(就像你在英语中使用复数一样)。

于 2011-11-22T21:59:24.607 回答