Большинство Хаскелл уроки учить использовать do-нотацию для ИО.
Я тоже начинал с do-нотацию, но что делает мой код более похожим на императивный язык больше, чем язык ФП.
На этой неделе я видел, как учебник использовать IO с <$>
stringAnalyzer <$> readFile "testfile.txt"
вместо использования делать
main = do
strFile <- readFile "testfile.txt"
let analysisResult = stringAnalyzer strFile
return analysisResult
И инструмент анализа журнала готовой без "делать".
Так что мой вопрос есть "следует избегать do-нотацию в любом случае?&и".
Я знаю, что может "сделать" сделает код лучше в некоторых случаях.
Кроме того, почему большинство обучающих Ио с "делать"?
На мой взгляд в <$>
и в <*>
делает код более ФП, чем ИО.
"делать" нотация в Haskell desugars в довольно простой способ.
do
x <- foo
e1
e2
...
превращается в
foo >>= \x ->
do
e1
e2
и
do
x
e1
e2
...
в
x >>
do
e1
e2
....
Это означает, что вы можете действительно написать любой монадические вычисления с >>=
и возвращение
. Единственная причина, почему мы не'т потому что это's просто более болезненно синтаксис. Монады являются полезными для подражания императивный код, "делать" нотации делает его похожим.
С-иш синтаксис делает его гораздо легче для начинающих, чтобы понять это. Вы'вновь правы, это не'т выглядеть так функциональна, но требует, чтобы кто-то обращал внимания должным образом монады, прежде чем они могут использовать IO-это очень большой сдерживающий фактор.
Причина, почему мы'd с помощью >>=
и возвращение
с другой стороны, потому что он'ы гораздо более компактная для 1 - 2 вкладышей. Однако это, как правило, чтобы получить немного больше нечитаю ничего слишком большой. Так, чтобы прямо ответить на ваш вопрос, Нет, пожалуйста, Дон'т избежать делать нотации, когда это необходимо.
Наконец, два оператора вы видели, в <$>
и в <*> У
, на самом деле fmap и прикладных соответственно, не монадическом. Они могут'т действительно использоваться, чтобы представить многое из того, что делать нотации делает. Они'повторно более компактным, чтобы быть уверенным, но они не'т давайте вы с легкостью промежуточных значений. Лично я пользуюсь им около 80% времени, в основном потому, что я обычно пишу очень маленькие составные функции в любом случае, которая прозрачна для.
В мое мнение в
<$>
и в<*>
делает код более ФП, чем ИО.
Хаскелл-это не чисто функциональный язык, потому что "выглядит лучше и". Иногда она это делает, часто это не'т. Причиной оставаясь функциональным не является его синтаксис, но его semantics. Она вооружает нас с ссылочной прозрачности, что делает его гораздо легче доказать инварианты, позволяет очень высокого уровня оптимизации, позволяет легко общего назначения писать код и т. д..
Ничего из этого не имеет много общего с синтаксисом. Монадические вычисления по-прежнему чисто функционально – независимо от того, вы пишете им "делать" нотации или С в <$>
, в <*>
и >>=
, таким образом мы получаем Хаскелл's выгоды в любом случае.
Однако, несмотря на вышеупомянутые ФП-преимущества, он часто более интуитивным, чтобы думать об алгоритмах с императивом-как точку зрения – даже если вы'вновь привыкли к тому, как это реализуется посредством монад. В этих случаях "делать" нотация дает вам быстрое представление о том, что "Порядок исчисления и", то "данные" В, С "точки изменения с", но это'ы банально вручную desugar это в голову >>=
версии, чтобы понять, что'ы происходит функционально.
Аппликативный стиль-это конечно здорово во многих отношениях, однако она по сути своей точке-бесплатный. Это часто хорошая вещь, но особенно в более сложные проблемы, это может быть очень полезно давать имена, чтобы "временный" переменных. При использовании лишь "ПС" и синтаксиса языка Haskell, это требует либо лямбда или явно именованные функции. У обоих есть хорошие примеры, но бывшие вводит довольно много шума в середине кода и последнее, а срывает на "Поток" и, поскольку она требует Где
или давай
перенести в другое место от того, где вы его используете. "делать", с другой стороны, позволяет ввести именованную переменную там, где это необходимо, без каких-либо шума.
Я часто нахожу себя в первую очередь пишу монадических действий В делать
нотации, потом рефакторинг это сводится к простой монаде (или functorial) выражение. Это происходит в основном, когда "делать" блок оказывается короче, чем я ожидал. Иногда я рефакторинг в противоположном направлении; это зависит от кода в вопрос.
Мое основное правило: если "делать" блок только пару строк он'ы, как правило, аккуратнее, как короткое выражение. Долго "делать" -блок, вероятно, более читаемым, как это, если вы можете найти способ, чтобы разбить его на более мелкие, более составного функции.
Как пример, здесь's, как мы можем превратить ваш многословный фрагмент кода в ваш простой.
main = do
strFile <- readFile "testfile.txt"
let analysisResult = stringAnalyzer strFile
return analysisResult
Во-первых, заметить, что последние две строки имеют вид пусть X = Y в возврат х
. Это, конечно, может быть преобразован в просто возвращение г
.
main = do
strFile <- readFile "testfile.txt"
return (stringAnalyzer strFile)
Это очень короткий "сделать" блок: мы привязать функцию ReadFile "с вопросами и ответами.тхт"` в имени, а потом делать то, что наименование в следующей строке. Позвольте'попробуем 'де-шугаринг' это как компилятор:
main = readFile "testFile.txt" >>= \strFile -> return (stringAnalyser strFile)
Посмотри на лямбда-форму на правой стороне >>=
. Это'ы умоляя переписать в бесточечный стиль: \х -> ф $ г х
будет \х -> (Ф . г) х
что будет `Ф . г'.
main = readFile "testFile.txt" >>= (return . stringAnalyser)
Это уже много аккуратнее, чем оригинал и не заблокировать, но мы можем идти дальше.
Здесь's не единственный шаг, который требуется немного подумать (хотя после того, как вы're с монады и функторы это должно быть очевидно). Эта функция суггестивная одного из монада законы: (м >>= возврат) == м
. Единственная разница заключается в том, что функция в правой части >>=
это'Т просто возвращение
- мы что-то делаем с объектом внутри монады перед тем, как завернуть его обратно в вернуться
. Но картина 'делаешь что-то обернутое значение, не влияя на ее фантик' это именно то, что функтор
для. Все монады являются функторы, поэтому мы можем выполнить рефакторинг это так, что мы не'т даже нужно Монада
экземпляр:
main = fmap stringAnalyser (readFile "testFile.txt")
Наконец, обратите внимание, что в <$>
это просто еще один способ написания fmap
.
main = stringAnalyser <$> readFile "testFile.txt"
Я думаю, что эта версия является намного более четким, чем исходный код. Это можно прочитать как приговор: "в основном
это stringAnalyser
применяется к результату значение в "вопросами и ответами.тхт"
в лице". Оригинальная версия болот вас в процедурные детали своей работы.
Добавление: мой комментарий, что 'все монады являются функторы' На самом деле может быть оправдано замечание, что М >>= (возврат . Ф)
(ака стандартная библиотека'liftM ы`) так же, как fmap Ф М
. Если у вас есть экземпляр Монада
, вы получите экземпляр функтор
'бесплатно' - такое определение fmap = liftM! Если кто-то's, определяемым в
Монадаэкземпляр для своего типа, но не экземпляры для
функтори
прикладной, я'd не называю это ошибка. Клиенты ожидают, чтобы иметь возможность использовать методы
функторэкземпляров
Монада` без лишней нервотрепки.
мы должны избежать делать записи в любом случае?
Я'd не сказать точно нет. Для меня самым важным критерием в таких случаях является для того чтобы сделать код читаемым и понятным, как много, как это возможно. "До" -нотация была введена, чтобы сделать монадическом код более понятным, и это главное. Конечно, во многих случаях, используя прикладных
точка-бесплатная нотации чрезвычайно приятно, например, вместо
do
f <- [(+1), (*7)]
i <- [1..5]
return $ f i
мы'буду писать просто[(+1), (*7)] <*> [1..5]
.
Но есть много примеров, где не с помощью "сделать" -нотация будет сделать код очень нечитабелен. Считают это пример:
nameDo :: IO ()
nameDo = do putStr "What is your first name? "
first <- getLine
putStr "And your last name? "
last <- getLine
let full = first++" "++last
putStrLn ("Pleased to meet you, "++full++"!")
вот он'вполне понятно, что'ы происходит и как действия ввода-вывода упорядочиваются. А "делать" -бесплатная нотации выглядит
name :: IO ()
name = putStr "What is your first name? " >>
getLine >>= f
where
f first = putStr "And your last name? " >>
getLine >>= g
where
g last = putStrLn ("Pleased to meet you, "++full++"!")
where
full = first++" "++last
или как
nameLambda :: IO ()
nameLambda = putStr "What is your first name? " >>
getLine >>=
\first -> putStr "And your last name? " >>
getLine >>=
\last -> let full = first++" "++last
in putStrLn ("Pleased to meet you, "++full++"!")
которые являются гораздо менее читабельным. Конечно, здесь "делать" -нотация гораздо предпочтительнее здесь.
Если вы хотите, чтобы избежать использования делать
, попробуйте структурировать свой код на множество мелких функций. Это хорошая привычка в любом случае, и вы можете уменьшить ваш делать
заблокировать, чтобы только содержать 2-3 строчки, который может быть затем заменен приятно >>=
, <$> В, в
<*>` и т. д. Например, выше может быть переписан как
name = getName >>= welcome
where
ask :: String -> IO String
ask s = putStr s >> getLine
join :: [String] -> String
join = concat . intersperse " "
getName :: IO String
getName = join <$> traverse ask ["What is your first name? ",
"And your last name? "]
welcome :: String -> IO ()
welcome full = putStrLn ("Pleased to meet you, "++full++"!")
Это'ы немного дольше, и, возможно, немного менее понятны Хаскелл новичкам (из-за смешивать
, функция concat
и траверс
), но во многих случаях эти новые небольшие функции могут быть повторно использованы в других местах кода, которая позволит сделать его более структурированным и составного.
Я'd не сказать, что ситуация очень похожа на, Следует ли использовать бесточечный обозначения или нет. Во многих случаях (как в верхнем примере[(+1), (*7)] <*> [1..5]
) точки-бесплатный нотации-это здорово, но если вы попытаетесь преобразовать сложное выражение, вы получите результаты, как
f = ((ite . (<= 1)) `flip` 1) <*>
(((+) . (f . (subtract 1))) <*> (f . (subtract 2)))
where
ite e x y = if e then x else y
Это'д возьми меня довольно много времени, чтобы понять это без выполнения кода. [Спойлер ниже:]
!
F х = Если (х <= 1) тогда 1 иначе Ф (Х-1) + f (х-2)
также, почему большинство обучающих Ио с делать?
Потому что Ио
- это точно конструирована для того чтобы передразнить важно вычислений с побочными эффектами, и поэтому последовательность их использование не очень естественно.
Аппликативный стиль следует поощрять, потому что он сочиняет (и красивее). Монадическом стиле необходимо в определенных случаях. См https://stackoverflow.com/a/7042674/1019205 подробное объяснение.
"делать" нотации-это просто синтаксический сахар. Это может быть избегать во всех случаях. Однако, в некоторых случаях заменяя делать
с >>=
и возвращение
делает код менее читабельным.
Итак, на ваши вопросы:
"Мы должны избегать заявление в любом случае?&и".
Сосредоточиться на коде ясным и лучше читаемым. Используйте "сделать", когда он помогает, не иначе.
> и у меня еще вопрос, почему большинство учебников будет учить Ио с делать?
Потому что "делать" ИО делает код лучше читаемым во многих случаях.
Кроме того, большинство людей, кто начинает учить Хаскелл имеют императивного программирования. Уроки для начинающих. Они должны использовать стиль, который легко разобраться новичкам.
Делать нотации расширяется до выражения с помощью функции
(>>=)и
(>>), И
да` выражение. Поэтому он не является частью ядра языка.
(>>=)
и (>>)
используются, чтобы объединить действия последовательно и они имеют значение, когда результат действия изменяет структуру следующие действия.
В примере, приведенном в вопросе, это не очевидно, поскольку есть только одно `действие ввода-вывода, поэтому никакой последовательности не требуется.
Рассмотрим, например, выражение
do x <- getLine
print (length x)
y <- getLine
return (x ++ y)
что в переводе с
getLine >>= \x ->
print (length x) >>
getLine >>= \y ->
return (x ++ y)
В этом примере "делать" записи (или о (>>=)
и (>>)
функции) требуется для последовательности действий ввода-вывода.
Так что рано или поздно программист понадобится.