Во-первых, синтаксис.Пробел - это приложение , семантически:
f x = f $ x -- "call" f with an argument x
, поэтому ваше выражение на самом деле
numericBinop op params = ((mapM unpackNum) params) >>= return . Number . (foldl1 op)
Далее, операторы построены из не алфавитно-цифровых символов, без пробелов,Здесь есть .
и >>=
.Запуск :i (.)
и :i (>>=)
в GHCi показывает их фиксированность спецификации infixl 9 .
и infixr 1 >>=
.9
выше 1
, поэтому .
сильнее >>=
;таким образом,
= ((mapM unpackNum) params) >>= (return . Number . (foldl1 op))
infixl 9 .
означает .
, связанное справа, таким образом, наконец, это
= ((mapM unpackNum) params) >>= (return . (Number . (foldl1 op)))
(.)
определяется как (f . g) x = f (g x)
, таким образом (f . (g . h)) x = f ((g . h) x) = f (g (h x)) = (f . g) (h x) = ((f . g) . h) x
;при сокращении eta мы имеем
(f . (g . h)) = ((f . g) . h)
, таким образом, (.)
является ассоциативным, и поэтому заключение в скобки необязательно.Теперь мы будем отбрасывать явные символы с помощью приложения «пробел».Таким образом, у нас есть
numericBinop op params = (mapM unpackNum params) >>=
(\ x -> return (Number (foldl1 op x))) -- '\' is for '/\'ambda
. Монадические последовательности легче записать с помощью do
, а приведенное выше эквивалентно
= do
{ x <- mapM unpackNum params -- { ; } are optional, IF all 'do'
; return (Number (foldl1 op x))) -- lines are indented at the same level
}
Далее, mapM
можно определить как
mapM f [] = return []
mapM f (x:xs) = do { x <- f x ;
xs <- mapM f xs ;
return (x : xs) }
и Законы Монады требуют, чтобы
do { r <- do { x ; === do { x ;
y } ; r <- y ;
foo r foo r
} }
(вы можете найти краткий обзор do
обозначений, например, в этом недавнем ответе моего);таким образом,
numericBinop op [a, b, ..., z] =
do {
a <- unpackNum a ;
b <- unpackNum b ;
...........
z <- unpackNum z ;
return (Number (foldl1 op [a, b, ..., z]))
}
(вы могли заметить, что я использовал x <- x
привязок - мы можем использовать одно и то же имя переменной с обеих сторон <-
, потому что монадные привязки не рекурсивно - таким образом, вводя теневое копирование.)
Теперь это стало понятнее, надеюсь.
Но я сказал: " first , синтаксис".Итак, теперь смысл этого.По тем же законам Монады,
numericBinop op [a, b, ..., y, z] =
do {
xs <- do { a <- unpackNum a ;
b <- unpackNum b ;
...........
y <- unpackNum y ;
return [a, b, ..., y] } ;
z <- unpackNum z ;
return (Number (op (foldl1 op xs) z))
}
, таким образом, нам нужно только понять последовательность двух"вычислений", c
и d
,
do { a <- c ; b <- d ; return (foo a b) }
=
c >>= (\ a ->
d >>= (\ b ->
return (foo a b) ))
для конкретной участвующей монады, которая определяется реализацией оператора bind (>>=
) для данной монады.
Монады - это EDSL для композиции обобщенных функций.Последовательность вычислений включает в себя не только явные выражения, появляющиеся в последовательности do
, но также неявные эффекты, свойственные конкретной рассматриваемой монаде, выполняемые принципиально и согласованно за кулисами.В этом и заключается весь смысл наличия монад в первую очередь (ну, по крайней мере, одного из основных моментов).
Здесь, по-видимому, рассматриваемая монада озабочена возможностью провала и ранних спасенийв случае, если сбой действительно произойдет.
Итак, с помощью кода do
мы пишем сущность того, что мы намереваемся случиться, и возможность прерывистого отказа автоматически рассматривается для нас за кулисами.
Другими словами, если одно из unpackNum
вычислений не удастся , то все объединенное вычисление завершится неудачей, не пытаясь выполнить любое из следующих unpackNum
суб-вычислений.Но если все они преуспеют, то комбинированные вычисления также будут успешными.