redbneb «s answer почти сразу же, за исключением того, что для Монады две временные рамки являются приобщенных, что является их сущность;
Расчет Haskell делает после того, как внешний мир предоставил некоторые входы, скажем, на предыдущем этапе вычисления; затем построить рецепт ⁄ «описания вычислений», который затем работает в свою очередь. В противном случае это была бы не Монада, а Аппликация, которая строит свои рецепты ⁄ описания из известных ранее компонентов.
И смиренное Functor сами уже имеет две временных шкал (что его сущности): IO a
значения описывает «внешний мир» ⁄ будущих IO-вычисления «производство» в «внутри» ⁄ чистом a
результата.
Рассмотрим:
[f x | x <- xs] f <$> xs Functor [r | x<-xs,r<-[f x]]
[y x | y <- f, x <- xs] f <*> xs Applicative [r | y<-f,x<-xs,r<-[y x]]
[r | x <- xs, r <- f x] f =<< xs Monad [r | x<-xs,r<- f x ]
(написано с монадными постижениями). Конечно, Functor (Applicative/Monad/...) также может быть чистым; еще есть два временных графика ⁄ «миры».
Несколько конкретных примеров:
~> [x*2 | x<-[10,100]]
~> [r | x<-[10,100], r <- [x*2]] -- non-monadic
[20,200] -- (*2) <$> [10,100]
~> [x*y | x<-[10,100], y <- [2,3]]
~> [r | x<-[10,100], y <- [2,3], r <- [x*y]] -- non-monadic
[20,30,200,300] -- (*) <$> [10,100] <*> [2,3]
~> [r | x<-[10,100], y <- [2,3], r <- replicate 2 (x*y) ]
~> [r | x<-[10,100], y <- [2,3], r <- [x*y, x*y]] -- still non-monadic:
~> (\a b c-> a*b) <$> [10,100] <*> [2,3] <*> [(),()] -- it's applicative!
[20,20,30,30,200,200,300,300]
~> [r | x<-[10,100], y <- [2,3], r <- [x*y, x+y]] -- and even this
~> (\a b c-> c (a*b,a+b)) <$> [10,100] <*> [2,3] <*> [fst,snd] -- as well
~> (\a b c-> c a b) <$> [10,100] <*> [2,3] <*> [(*),(+)]
[20,12,30,13,200,102,300,103]
~> [r | x<-[10,100], y <- [2,3], r <- replicate y (x*y) ] -- only this is _essentially_
~> [10,100] >>= \x-> [2,3] >>= \y -> replicate y (x*y) -- monadic !!!!
[20,20,30,30,30,200,200,300,300,300]
По существу-монадические вычисления строятся из шагов, которые не могут быть построены перед объединенной вычислением в время выполнения, потому, что рецепт построения определяется величиной в результате полученной ранее вычисленной величины стоимости, полученной по расчёту рецепта, когда он фактически выполнен.
Уставившись на следующем изображении может оказаться illuminating:
Btw - где вы видите это? Наличие контекста поможет в формулировании ответа. – ErikR
Хорошо, IO выполняет время для выполнения, а также взаимодействие с пользователем, например _getLine_, которое не всегда, но имеет тенденцию иметь побочный эффект. Как это может быть чисто? –
Первым шагом к пониманию того, как управляется ввод-вывод, является распознавание того, что 'getLine' является * not * функцией ввода-вывода. Это константа с типом 'IO String', которая * представляет * программу, которая может выполнять операции ввода-вывода. – chepner