Как я организую свои чистые функции с моими монадическими действиями идиоматически - PullRequest
17 голосов
/ 23 ноября 2011

Сегодня я решил, что это день, когда я исправлю некоторые из моих чистых функций, которые излишне выполняются в монадическом действии.Вот что у меня есть.

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 являются монадическими действиями.Я не уверен, как сочетать монадические действия с чистыми функциями, идиоматически в flagWorkDays.Может ли кто-нибудь помочь мне исправить flagWorkDays, если предположить, что flagWeekEnds и flagHolidays сделаны чистыми?

Ответы [ 3 ]

29 голосов
/ 23 ноября 2011

Давайте сделаем шаг назад на мгновение.У вас есть два типа функций, некоторые чисто с типами формы a -> b, а некоторые монадические типа a -> m b.

Чтобы избежать путаницы, давайте также придерживаться композиции справа налево.Если вы предпочитаете читать слева направо, просто измените порядок функций и замените (<=<) на (>=>), а (.) на (>>>) из 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. Monadic, а затем monadic .Используйте композицию Клейсли (<=<).

     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

И все готово.

5 голосов
/ 23 ноября 2011

Чтобы заполнить ответ FUZxxl, давайте очистим flagWeekEnds:

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

Вы часто ставите «s» после имен переменных (day -> days), когда это список (как вы делаете это во множественном числе на английском языке).

5 голосов
/ 23 ноября 2011

Это не очень сложно.Вы просто заменяете (>>=) на (.) и меняете порядок операндов.do синтаксис может помочь уточнить.Я также сделал пример бессмысленным, используя комбинатор Клейсли (рыба) (<=<) :: (b -> m c) -> (a -> m b) -> a -> m c, то есть по существу (.) для монад.

import Control.Monad

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