Изучай Haskell во имя добра! (Липовача) - страница 235

, распространяя недетерминированность:

>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) в контекст по умолчанию (одноэлементный список) для возврата его в качестве результата без привнесения какой-либо дополнительной недетерминированности. В генераторе списка произошло то же самое, но нам не нужно было писать вызов функции