, распространяя недетерминированность:
>ghci> [1,2] >>= \n –> ['a','b'] >>= \ch –> return (n,ch)
>[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
Числа из списка >[1,2]
связываются с образцом >n
; символы из списка >['a','b']
связываются с образцом >ch
. Затем мы выполняем выражение >return (n, ch)
(или >[(n, ch)]
), что означает получение пары >(n, ch)
и помещение её в минимальный контекст по умолчанию. В данном случае это создание наименьшего возможного списка, который по-прежнему представляет пару >(n, ch)
в качестве результата и обладает наименее возможной недетерминированностью. Его влияние на контекст минимально. Мы говорим: «Для каждого элемента в списке >[1,2]
обойти каждый элемент из >['a','b']
и произвести кортеж, содержащий по одному элементу из каждого списка».
Вообще говоря, поскольку функция >return
принимает значение и оборачивает его в минимальный контекст, она не обладает какими-то дополнительными эффектами (вроде приведения к неуспешному окончанию вычислений в типе >Maybe
или получению ещё большей недетерминированности для списков), но она действительно возвращает что-то в качестве своего результата.
Когда ваши недетерминированные значения взаимодействуют, вы можете воспринимать их вычисление как дерево, где каждый возможный результат в списке представляет отдельную ветку. Вот предыдущее выражение, переписанное в нотации >do
:
>listOfTuples :: [(Int,Char)]
>listOfTuples = do
> n <– [1,2]
> ch <– ['a','b']
> return (n,ch)
Такая запись делает чуть более очевидным то, что образец >n
принимает каждое значение из списка >[1,2]
, а образец >ch
– каждое значение из списка >['a','b']
. Как и в случае с типом >Maybe
, мы извлекаем элементы из монадического значения и обрабатываем их как обычные значения, а операция >>>=
беспокоится о контексте за нас. Контекстом в данном случае является недетерминированность.
Нотация do и генераторы списков
Использование списков в нотации >do
может напоминать вам о чём-то, что вы уже видели ранее. Например, посмотрите на следующий кусок кода:
>ghci> [(n,ch) | n <– [1,2], ch <– ['a','b']]
>[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
Да! Генераторы списков! В нашем примере, использующем нотацию >do
, образец >n
принимал значения всех результатов из списка >[1,2]
. Для каждого такого результата образцу >ch
был присвоен результат из списка >['a','b']
, а последняя строка помещала пару >(n,
>ch)
в контекст по умолчанию (одноэлементный список) для возврата его в качестве результата без привнесения какой-либо дополнительной недетерминированности. В генераторе списка произошло то же самое, но нам не нужно было писать вызов функции