Как я могу организовывать свои чистые функции с моими монадическими действиями идиоматически
Сегодня я решил, что я исправлю некоторые из моих чистых функций, которые излишне выполняются в монадическом действии. Вот что у меня есть.
flagWorkDays :: [C.Day] -> Handler [WorkDay]
flagWorkDays dayList =
flagWeekEnds dayList >>=
flagHolidays >>=
flagScheduled >>=
flagASAP >>=
toWorkDays
Вот флагWeekEnds, на данный момент.
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
были сделаны чистыми?
Ответы
Ответ 1
Позвольте сделать шаг назад на мгновение. У вас есть два типа функций, некоторые чистые с типами формы a -> b
и некоторые монадические типа a -> m b
.
Чтобы избежать путаницы, давайте также придерживаться композиции справа налево. Если вы предпочитаете читать слева направо, просто измените порядок функций и замените (<=<)
на (>=>)
и (.)
на (>>>)
от Control.Arrow
.
Есть четыре возможности для их компоновки.
-
Чистый, чистый. Используйте регулярную композицию функций (.)
.
g :: a -> b
f :: b -> c
f . g :: a -> c
-
Чистый, затем монадический. Также используйте (.)
.
g :: a -> b
f :: b -> m c
f . g :: a -> m c
-
Монадический, затем монадический. Используйте композицию kleisli (<=<)
.
g :: a -> m b
f :: b -> m c
f <=< g :: a -> m c
-
Монадический, тогда чистый. Используйте 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
И все готово.
Ответ 2
Это не очень сложно. Вы просто замените (>>=)
на (.)
и переверните порядок операндов. Синтаксис 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
Ответ 3
Чтобы заполнить ответ FUZxxl, пусть pureify flagWeekEnds
:
flagWeekEnds :: [C.Day] -> [(C.Day,Availability)]
flagWeekEnds days = days `zip` map (flagAvailability . isWorkDay) days
Вы часто ставите "s" после имен переменных (day
→ days
), когда его список (как вы делаете с множественным числом на английском языке).