Предикат берёт элемент списка и возвращает значение типа Bool
Bool
было на самом деле монадическим? Что если к нему был приложен контекст?.. Например, каждое значение True
или False
, произведённое предикатом, имело также сопутствующее моноидное значение вроде ["Принято число 5"]
или ["3 слишком мало"]
? Если бы это было так, мы бы ожидали, что к результирующему списку тоже прилагается журнал всех журнальных значений, которые были произведены на пути. Поэтому если бы к списку, возвращённому предикатом, возвращающим значение типа Bool
, был приложен контекст, мы ожидали бы, что к результирующему списку тоже прикреплён некоторый контекст. Иначе контекст, приложенный к каждому значению типа Bool
, был бы утрачен.Функция filterM
Control.Monad
делает именно то, что мы хотим! Её тип таков:filterM :: (Monad m) => (a –> m Bool) –> [a] –> m [a]
Предикат возвращает монадическое значение, результат которого – типа Bool
Давайте возьмём список и оставим только те значения, которые меньше 4
filter
:ghci> filter (\x –> x < 4) [9,1,5,2,10,3]
[1,2,3]
Это довольно просто. Теперь давайте создадим предикат, который помимо представления результата True
False
также предоставляет журнал своих действий. Конечно же, для этого мы будем использовать монаду Writer
:keepSmall :: Int –> Writer [String] Bool
keepSmall x
| x < 4 = do
tell ["Сохраняем " ++ show x]
return True
| otherwise = do
tell [show x ++ " слишком велико, выбрасываем"]
return False
Вместо того чтобы просто возвращать значение типа Bool
Writer [String] Bool
. Это монадический предикат. Звучит необычно, не так ли? Если число меньше числа 4
, мы сообщаем, что оставили его, а затем возвращаем значение True
.Теперь давайте передадим его функции filterM
Writer
, результирующий список также будет значением типа Writer
.ghci> fst $ runWriter $ filterM keepSmall [9,1,5,2,10,3]
[1,2,3]
Проверяя результат результирующего значения монады Writer
ghci> mapM_ putStrLn $ snd $ runWriter $ filterM keepSmall [9,1,5,2,10,3]
9 слишком велико, выбрасываем
Сохраняем 1
5 слишком велико, выбрасываем
Сохраняем 2
10 слишком велико, выбрасываем
Сохраняем 3
Итак, просто предоставляя монадический предикат функции filterM
Очень крутой трюк в языке Haskell – использование функции filterM
[1,2,3]
, его множество-степень включает следующие множества:[1,2,3]
[1,2]
[1,3]
[1]
[2,3]
[2]
[3]
[]
Другими словами, получение множества-степени похоже на получение всех сочетаний сохранения и выбрасывания элементов из множества. Например, [2,3]
1
; [1,2]
– это исходное множество с исключением числа 3
и т. д.Чтобы создать функцию, которая возвращает множество-степень какого-то списка, мы положимся на недетерминированность. Мы берём список [1,2,3]
1
, и спрашиваем себя: «Должны ли мы его сохранить или отбросить?» Ну, на самом деле мы хотели бы сделать и то и другое. Поэтому мы отфильтруем список и используем предикат, который сохраняет и отбрасывает каждый элемент из списка недетерминированно. Вот наша функция powerset
:powerset :: [a] –> [[a]]
powerset xs = filterM (\x –> [True, False]) xs