5

Я очень новичок в Haskell, и на самом деле впечатлен «архитектурой» языка, но мне все еще мешает, как monads может быть чистым.Как монады считаются чистыми?

Как вы уже любой последовательности инструкций, это делает его нечистая функция, особенно функции с I/O не будет чиста от любой точки зрения.

Это потому, что Haskell предполагает, что, как и все чистые функции, функция IO имеет также возвращаемое значение, но в виде кода операции или что-то в этом роде? Я действительно смущен.

+0

Btw - где вы видите это? Наличие контекста поможет в формулировании ответа. – ErikR

+0

Хорошо, IO выполняет время для выполнения, а также взаимодействие с пользователем, например _getLine_, которое не всегда, но имеет тенденцию иметь побочный эффект. Как это может быть чисто? –

+1

Первым шагом к пониманию того, как управляется ввод-вывод, является распознавание того, что 'getLine' является * not * функцией ввода-вывода. Это константа с типом 'IO String', которая * представляет * программу, которая может выполнять операции ввода-вывода. – chepner

ответ

18

Один способа думать об этом является то, что значение типа IO a является «рецептом», содержащей список инструкций, которые в случае их выполнения будут иметь побочные эффекты. Построить этот «рецепт», хотя и не имеет никаких побочных эффектов. Таким образом, программа haskell (тип которой IO()) представляет собой в основном вычисление, которое создает такой рецепт. Важно отметить, что программа не выполняет никаких инструкций в рецепте. Когда рецепт завершен, программа завершается. Затем компилятор (или интерпретатор) принимает этот рецепт и выполняет его. Но код, написанный программистом, больше не работает, поэтому инструкции в рецепте выполнены вне рамок программы.

+0

Просто, это чисто, потому что (1) он даст один и тот же вывод для одного и того же входа и (2), и при оценке он не имеет побочных эффектов при других вычислениях. – Bassam

9

Один из способов, которым «монады могут быть чистыми», состоит в том, что они могут представлять собой чистые выражения. То есть в списке Монада это:

do x <- xs 
    return (x+1) 

такое же как map (+1) xs.

Другим способом вы можете сказать: «монада может быть чистой» в том, что Haskell различает создание монадических вычисления и работает вычисления.

Создание монадических вычислений является чистым и не включает никаких побочных эффектов. Например, replicateM 3 foo будет выполнять foo три раза , как в replicateM 3 (putStrLn "Hello, world"). Чистота позволяет нам рассуждать , что replicateM 3 foo - это то же самое, что и у do { foo; foo; foo }. Обратите внимание, что это верно независимо от того, что такое вычисление foo - это может быть чистое вычисление или одно, которое предполагает какой-то эффект.

Побочные эффекты возникают только тогда, когда вы бегите монадические вычисления. В случае с монадой ввода-вывода это происходит во время выполнения, когда выполняется main. Другие монады имеют свою собственную «прогоны» функцию, т.е. runReader для чтения монады, runState для государства монады и т.д.

7

Монады не считаются чистыми или нечистыми. Это совершенно не связанные понятия. Ваше название похоже на вопрос, как глаголы считаются вкусными.

«Монада» относится к определенному шаблону композиции, который может быть реализован на типах с определенными конструкторами более высокого класса. Полностью концепция привязана к типам операций пары и правилам того, как эти операции должны взаимодействовать с собой и друг с другом.

Несколько языков могут выразить концепцию с пользой, поскольку она настолько абстрактна. Единственным относительно распространенным языком, кроме Haskell, может быть Scala.И это на самом деле относительно распространено в Scala, хотя они почему-то называют это flatMap. Неудивительно, что некоторые типы, которые поддерживают flatMap в Scala, не являются чистыми. Они правильно поддерживают flatMap 100%, и они не чисты.

Концепции просто не коррелированы.

Теперь, все, что сказал, я понимаю, откуда вы. Почти каждая статья в Haskell, которую вы видите, использует такие фразы, как «монашка IO» или «использует монады для управления эффектами» или другие подобные вещи. Дело в том, что любое использование такой терминологии глубоко вводит в заблуждение.

IO это тип. Это то, что отличается от нечистых языков. Операции ввода-вывода - значения определенного типа. Именно это позволяет Haskell оставаться принципиальным (в некотором роде) в отношении чистоты, даже взаимодействуя с внешним миром. Значения определенного типа построены для описания взаимодействия с внешним миром. Эти значения чисты, как описано в других ответах.

Итак, где Монада вписывается во все это? Ну, значения IO необходимо объединить вместе для создания более сложных операций ввода-вывода из более простых. И получается, что они сочетают в себе именно тот тип композиции, который описывает интерфейс Monad. Но так объединяются списки с flatMap, или Option значениями с andThen.

Акцент на Monad как на нечто важное, особенное или интересное вредит репутации Haskell и его возможности для новичков. Тем более, что это не важно, особенное или интересное. Лучшее сравнение, которое я могу сделать, - Iterator в Java. Да, язык имеет синтаксический сахар для работы с Monad/Iterator. Нет, что не приближается, подразумевая, что язык неприступен, если вы заранее не знаете концепцию или что есть глубокий смысл, который необходим для входа в какое-то сверхсекретное общество просвещения. Когда дело доходит до этого, ни одна идея не является очень глубокой или удивительной. Они очень широко применяются, простые идеи, с которыми легче работать, когда у вас есть немного синтаксического сахара под рукой.

+0

Это должен быть принятый ответ. –

+0

_ «Монада» относится к определенному шаблону композиции, который может быть реализован на типах с определенными конструкторами более высокого типа. Это все, что нужно! Объясняется в одном предложении. Спасибо! – ftor

2

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:

enter image description here

+0

Я бы назвал это немного по-другому, потому что мое ментальное представление о рецепте может быть монадическим: «Добавьте соль по вкусу». – dfeuer

+0

@dfeuer Я не совсем понял вашу мысль. Я хочу сказать, что добавление соли не является монадическим. Если 'f' является« добавлением », а' a' является «солью», то 'f a' уже« добавляет соль », прежде чем мы поговорим о Monads или функторах. –

+0

Вы пропустили ключ «по вкусу». Вы пробуете блюдо, чтобы решить, нужно ли ему больше соли. Затем вы добавляете соль итеративно, пока вам больше не понадобится больше соли. – dfeuer