LINUX.ORG.RU

Зачем в Haskell IO?

 ,


0

2

Haskell язык ленивый. Для интерактивных программ достаточно вообще обычных строк:

main = interact realMain

realMain :: String -> String
realMain = unlines . f . lines
  where
    f x = "Введите своё имя: ":g x
    g (имя:числа) = ["Привет, " ++ имя, "Вводите числа, q для выхода"] ++ корни числа
    корни ("q":_) = []
    корни (x:xs) = (show . sqrt) (read x :: Double):корни xs

Вот программа, которая запрашивает имя пользователя, затем выводит корни введённых чисел пока не получит q. Никакого IO внутри realMain не требуется.

Если брать произвольное взаимодействие с окружающим миром, то достаточно main :: [inEvent] -> [outEvent].

Зачем нужен «магический» тип IO, делящий Haskell на фактически два разных языка?

★★★★★

Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от hateyoufeel

Ты блин почитай, на что сам ссылаешься :)

Ты не дочитал. Смотри чуть выше от «We can now have complete* confidence in the values returned from catchAny»

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Ну да, я про это же. Там буквально написано, что не надо использовать catch просто так. Вместо этого, надо использовать хрень, которую они запилили и которая уже есть в стандартной библиотеке.

hateyoufeel ★★★★★
()
Ответ на: комментарий от monk

Если речь о do синтаксисе, то от не IO-специфичен и является чистым синтаксичеким сахаром поверх лямбда функций

Нет. Я про несовместимость Int и IO Int.

Ага, понятно.

Добавился world / world’ / world'' boilerplate (и возможность случайно 2 раза использовать один из world).

Это именно из-за генератора вместо списка.

main events = (readFile "a"):g events
 where
   g (inString с:_) = [(writeFile "b" с)]

(inString с:_) / _ выглядит как (world', c) / world''. Я бы сказал, что это точный эквивалет явной передачи глобального состояния.

sf ★★★
()
Ответ на: комментарий от sf

Ага, понятно.

Я уже понял, что от того, что вместо IO Int будет outEvent, лучше не станет.

Я бы сказал, что это точный эквивалет явной передачи глобального состояния.

Со списками его по крайней мере генерировать можно. С другой стороны, можно сделать всю реализацию на Polysemy, а потом давать те же списки. Вот только трансляцию из IO и из списков в Polysemy вручную писать приходится.

monk ★★★★★
() автор топика
Ответ на: комментарий от sf

Я бы сказал, что это точный эквивалет явной передачи глобального состояния.

Но «возможность случайно 2 раза использовать один из world» не появилась.

monk ★★★★★
() автор топика
Ответ на: комментарий от monk

Я бы сказал, что это точный эквивалет явной передачи глобального состояния.

Но «возможность случайно 2 раза использовать один из world» не появилась.

В голову приходит случайное повторное использование списка:

main events = (readFile "a"):g events
 where
   -- a bug: should be "h events'", not "h events"
   g events@(inString с:events') = (writeFile "b" с):(readNextFile "c"):h events
   h (nextInString c:events') = ...
sf ★★★
()
Ответ на: комментарий от sf

Для этого надо явно сделать «events@…». Это случайно написать достаточно сложно.

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

main events = (readFile "a"):g events
 where
   -- должно быть g (inString с:events) = ...
   g (inString с:events') = (writeFile "b" с):(readNextFile "c"):h events

Но имя с штрихом тоже используется только если надо одновременно обращаться к варианту без штриха и со штрихом. То есть тоже маловероятно. А если писать g и h не внутри where, то ошибка вообще невозможна.

К слову, зато есть возможность обратиться к прошлому или позапрошлому событию. В IO никак, разве что явно копировать в какой-то журнал.

monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)

Зачем нужен «магический» тип IO, делящий Haskell на фактически два разных языка?

Чтобы последующее вычисление могло зависеть от результата предыдущего.

Upd:

Haskell язык ленивый.

А, точно, у нас же ленивость. Тогда, наверное, можно и без монад обойтись - строить все возможные варианты выполнения программы, и те ветви, которые не актуальны, просто будут незаметно отгнивать.

Laz ★★★★★
()
Последнее исправление: Laz (всего исправлений: 1)
Ответ на: комментарий от Laz

Чтобы последующее вычисление могло зависеть от результата предыдущего.

r = f . g . h даёт такую же зависимость, как и h >> g >> f.

В принципе, единственное место которое радикально улучшилось от IO: типизация синхронных операций. В списке нельзя указать, что если следующий элемент в исходящем списке чтение массива целых, то следующим элементом во входящем обязательно будет целое.

С другой стороны, с асинхронными работать гораздо проще.

monk ★★★★★
() автор топика
Ответ на: комментарий от Laz

Ну так интерес-то представляет именно >>=.

А в >>= вообще явно зависимость прописана.

do
  x <- f1
  y <- f2
  return x + y

и

  let x = f1 
      y = f2 x
  in x + y

дают аналогичную зависимость.

monk ★★★★★
() автор топика
Ответ на: комментарий от Laz

Чтобы последующее вычисление могло зависеть от результата предыдущего.

Пример программы в начале недостаточен?

main x = "Введите своё имя: ":g x
    g (имя:числа) = ["Привет, " ++ имя, "Вводите числа, q для выхода"] ++ корни числа
    корни ("q":_) = []
    корни (x:xs) = (show . sqrt) (read x :: Double):корни xs

против

main = do
  putStrLn "Введите своё имя: "
  имя <- getLine
  putStrLn $ "Привет, " ++ имя
  putStrLn "Вводите числа, q для выхода"
  getLine >>= корни
  where
  корни "q" = return ()
  корни x = do
    putStrLn $ (show . sqrt) (read x :: Double)
    getLine >>= корни
monk ★★★★★
() автор топика
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от monk

Пример программы в начале недостаточен?

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

Laz ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.