Промежуточные значения в haskell io

#haskell #io

#haskell #io

Вопрос:

Я изучаю Haskell, и в качестве упражнения я написал программу, которая считывает целое число и печатает следующее:

 main = do
    line_with_n_in_it <- getLine
    putStrLn $ show $ (read line_with_n_in_it :: Int)   1
  

Однако мне кажется довольно глупым, что я должен явно называть строку, которую я читаю.

Если я напишу

 main = do
    putStrLn $ show $ (read getLine :: Int)   1
  

runghc жалуется, что read ожидает String , но getLine предоставляет IO String . Итак, из того, что я понимаю, кажется, что этот волшебный <- оператор преобразуется IO String в String .

Есть ли другой оператор или функция, которые позволят мне просто встроить <- оператор? Так что, если бы magic была моей волшебной функцией, я смог бы написать свою новую программу как

 main = do
    putStrLn $ show $ (read $ magic getLine :: Int)   1
  

Комментарии:

1. fmap ( 1) readLn >>= print

2. Или, import Control.Applicative чтобы получить магию $ , иначе <$> , дающую ( 1) <$> getLine >>= print

Ответ №1:

Вам следует прочитать о монадах (и IO monad)

Здесь хорошее начало

Проблема в том, что вы должны «извлечь» значение из вашей монады, и это не «точно» вызов функции.

Ваш первый код правильный, вы извлекаете некоторое значение из monad

 readedString <- getLine
  

а затем используйте это

 putStrLn $ "Readed: "    readedString
  

вы можете избежать «именования строки», но, в общем, вполне нормально писать имена для этого.

чтобы избежать именования, вы должны написать некоторую монадическую функцию, а затем привязать

 getLine >>= putStrLn . show . ( 1) . read
  

но, опять же, я рекомендую вам прочитать о монадах (и IO monad).

Кстати, <- оператор «равен» >>= operator, подробности здесь.

Комментарии:

1. Это может быть немного не по теме — но почему вам не нужно комментировать тип read ? Поскольку это выглядит так, что ( 1) может быть применено как к Float , так и к Int , так как же haskell узнает, что выбрать?

2. @math4tots: Раздел 4.3.4 Неоднозначные типы и значения по умолчанию для перегруженных числовых операций : «Каждая переменная по умолчанию заменяется первым типом в списке по умолчанию, который является экземпляром всех классов неоднозначной переменной. . Значения по умолчанию — (Integer,Double) , и поскольку Integer это достаточно хорошо для этих операций, оно используется.

3. Здесь нет простого ответа 🙂 Haskell нужно знать конечный тип только при его использовании. В моей последней строке Haskell использует (по соглашению) Integer type (при записи появляется ошибка 3.14 ). Но «не окончательное» функциональное приложение является универсальным. Посмотрите на тип ( 1) . read :: (Num c, Read c) => String -> c .

Ответ №2:

Тот факт, что Haskell явно запрещает такое поведение, как раз и делает его таким ценным — вы не можете смешивать чистый код с кодом, пронизанным IO.

Волшебный <- оператор — это синтаксический сахар для монадической привязки. Например, ваша исходная функция может быть переписана явно с помощью >>= оператора (произносится «bind»):

 main = do
  n <- getLine
  putStrLn $ show $ (read n :: Int)   1

===

main = getLine >>= n -> putStrLn $ show $ (read n :: Int)   1
  

Тот факт, что <- вам это кажется волшебством, это хорошо — это не стандартный синтаксис haskell, это сахар. Последний способ написания функции должен иметь больше смысла.

Что касается того, почему это работает, я могу дать вам подробное объяснение, специфичное для монады ввода-вывода, и предложить вам прочитать главы о монадах Learn You a Haskell for Great Good! (или всю книгу, если вы действительно новичок!), чтобы углубить ваше понимание.

Поехали. getLine считывает String из IO , верно? Но это означает, что результат вызова «помечен IO»; это String завернутый в IO контекст, которым мы не можем манипулировать, как мы могли бы обычным String образом. Выполнение такой вещи нарушило бы ссылочную прозрачность, поскольку IO контекст (то есть то, что вы читаете) может измениться. По сути, что делает этот волшебный <- оператор, так это «вытаскивает» чистое String значение из IO контекста и позволяет вам оперировать с ним. Более явно, >>= оператор выполняет IO действие ( getLine ) и функцию, которая преобразует String в новое IO действие (лямбда-выражение в переведенном примере) и возвращает это новое IO действие. Это способ объединения IO выражений воедино и «копания» в их внутренних значениях, оперирования с ними и повторного преобразования их в IO . Монады в целом — это не то, что я (или кто-либо другой, если уж на то пошло) могу объяснить вам во всех подробностях в ответе SO, но я настоятельно рекомендую вам прочитать LYAH, если вы намерены продолжать изучать Haskell — это отличная книга, которая быстро введет вас в курс дела.