Преобразовать нотацию «DO» с более чем двумя действиями для использования функции связывания
Вопрос
Я знаю, что следующая функция "DO" Notation "Bind" эквивалентна getLine >>= \line -> putStrLn
do line <- getLine
putStrLn line
Но как следующая нотация эквивалентна для привязки функции?
do line1 <- getLine
putStrLn "enter second line"
line2 <- getLine
return (line1,line2)
Решение
Я так понимаю, вы пытаетесь увидеть, как связать результат "Putstrln". Ответ в типе Putstrln:
putStrLn :: String -> IO ()
Помните, что «()» - это тип блока, который имеет одно значение (также написано "()"). Таким образом, вы можете связать это точно так же. Но так как вы не используете его, вы связываете его с значением «не заботиться»:
getLine >>= \line1 ->
putStrLn "enter second line" >>= \_ ->
getline >>= \line2 ->
return (line1, line2)
Как это происходит, есть оператор, уже определенный для игнорирования возвращаемого значения, «>>». Так что вы могли бы просто переписать это как
getLine >>= \line1 ->
putStrLn "enter second line" >>
getline >>= \line2 ->
return (line1, line2)
Я не уверен, что вы также пытаетесь понять, как операторы связывания привязаны к магашкам. Чтобы увидеть это, позвольте мне поместить неявные кронштейны и дополнительное отступление в примере выше:
getLine >>= (\line1 ->
putStrLn "enter second line" >> (
getline >>= (\line2 ->
return (line1, line2))))
Каждый оператор связывания связывает значение слева с функцией справа. Эта функция состоит из всех остальных строк в пункте «DO». Таким образом, переменная, связанная через лямбда ("Line1" в первой строке), находится в области всей остальной части пункта.
Другие советы
Для этого конкретного примера вы можете избежать обоих do
и >>=
Используя комбинаторы из Control.Applicative
:
module Main where
import Control.Applicative ((<$>), (<*>), (<*))
getInput :: IO (String, String)
getInput = (,) <$> getLine <* putStrLn "enter second line" <*> getLine
main = print =<< getInput
Что работает как и ожидалось:
travis@sidmouth% ./Main
hello
enter second line
world
("hello","world")
Сначала это выглядит немного странно, но, на мой взгляд, стиль приложения кажется очень естественным, когда вы привыкли к этому.
getLine >>= \line1 ->
putStrLn "enter second line" >>
getLine >>= \line2 ->
return (line1, line2)
В целом foo <- bar
становится bar >>= \foo ->
и baz
становится baz >>
(Если это не последняя строка Do-Block, и в этом случае он просто остается baz
).
Я бы настоятельно предложил вам прочитать главу Отухание деулока В книге «Реальный мир Хаскелл». Он говорит вам, что вы все не правы. Для программиста это естественный способ использования лямбды, но Do -Block реализуется с использованием функций, которые - если произойдет неудача Maching, - вызовет fail
Внедрение Согласно Монаде.
Например, ваш случай похож на:
let f x =
putStrLn "enter second line" >>
let g y = return (x,y)
g _ = fail "Pattern mismatched"
in getLine >>= g
f _ = fail "Pattern mismatched"
in getLine >>= f
В таком случае это может быть совершенно неактуально. Но рассмотрим какое-то выражение, которое включает в себя сопоставление моделей. Кроме того, вы можете использовать этот эффект для некоторых специальных вещей, например, вы можете сделать что -то вроде этого:
oddFunction :: Integral a => [a] -> [a]
oddFunctiond list = do
(True,y) <- zip (map odd list) list
return y
Что будет делать эта функция? Вы можете прочитать это утверждение как правило для работы с элементами списка. Первый оператор связывает элемент списка с Var Y, но только если Y нечетный. Если y ровно, происходит сбой соответствия рисунка и fail
будет называться. В экземпляре Монады для списков, fail
это просто []
. Анкет Таким образом, функция устраняет все элементы из списка.
(Я знаю, oddFunction = filter odd
сделал бы это лучше, но это всего лишь пример)