Я думаю, вам нужно быть немного осторожнее с такими терминами, как «независимый» или «распараллеливаемый» или «зависимость». Например, в монаде IO рассмотрим вычисление:
foo :: IO (String, String)
foo = do
line1 <- getLine
line2 <- getLine
return (line1, line2)
Первая и вторая строки не являются независимыми или распараллеливаемыми в обычном смысле. На результат второго getLine
влияет действие первого getLine
через их общее внешнее состояние (т. Е. Первый getLine
читает строку, подразумевая, что второй getLine
не будет читать эту же строку, но будет скорее прочитайте следующую строку). Тем не менее, это действие является аппликативным:
foo = (,) <$> getLine <*> getLine
В качестве более реалистичного c примера, анализатор monadi c для выражения 3 + 4
может выглядеть следующим образом:
expr :: Parser Expr
expr = do
x <- factor
op <- operator
y <- factor
return $ x `op` y
Три действия здесь взаимозависимы. Успех первого парсера factor
определяет, будут ли запущены другие или нет, и его поведение (например, сколько входного потока он поглощает) явно влияет на результаты других парсеров. Было бы неразумно считать эти действия «параллельными» или «независимыми». Тем не менее, это аппликативное действие:
expr = factor <**> operator <*> factor
Или рассмотрим это State Int
действие:
bar :: Int -> Int -> State Int Int
bar x y = do
put (x + y)
z <- gets (2*)
return z
Очевидно, что результат действия gets (*2)
зависит от вычислений, выполненных в действие put (x + y)
. Но, опять же, это аппликативное действие:
bar x y = put (x + y) *> gets (2*)
Я не уверен, что существует действительно простой способ думать об этом интуитивно. Грубо говоря, если вы думаете о монади c действие / вычисление m a
как о «монади c структура» m
, а также «структура значения» a
, то аппликативные выражения сохраняют монади c и ценностные структуры раздельные. Например, аппликативное вычисление:
λ> [(1+),(10+)] <*> [3,4,5]
[4,5,6,13,14,15]
имеет структуру monadi c (список), в которой мы всегда имеем:
[f,g] <*> [a,b,c] = [f a, f b, f c, g a, g b, g c]
независимо от фактических значений. Следовательно, результирующая длина списка является произведением длины обоих «входных» списков, первый элемент результата включает в себя первые элементы «входных» списков и т. Д. c. Он также имеет структуру значений, благодаря которой значение 4
в результате явно зависит от значения (1+)
и значения 3
на входах.
A monadi c вычисления, с другой стороны, допускают зависимость структуры monadi c от структуры значений, поэтому, например, в:
quux :: [Int]
quux = do
n <- [1,2,3]
drop n [10..15]
мы не можем записать вычисление структурного списка независимо от значений , Структура списка (например, длина окончательного списка) зависит от данных уровня значений (фактические значения в списке [1,2,3]
). Это тот тип зависимости, который требует монады вместо аппликативного.