Преобразовать нотацию «DO» с более чем двумя действиями для использования функции связывания

StackOverflow https://stackoverflow.com/questions/3710991

  •  02-10-2019
  •  | 
  •  

Вопрос

Я знаю, что следующая функция "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 сделал бы это лучше, но это всего лишь пример)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top