Haskell: ввод / вывод и возврат из функции - PullRequest
6 голосов
/ 30 октября 2010

Пожалуйста, потерпите меня, так как я новичок в функциональном программировании и Haskell. Я пытаюсь написать функцию на Haskell, которая принимает список целых чисел, печатает заголовок указанного списка, а затем возвращает хвост списка. Функция должна иметь тип [Integer] -> [Integer]. Чтобы дать немного контекста, я пишу интерпретатор, и эта функция вызывается, когда соответствующая команда ищется в ассоциативном списке (ключ - это команда, значение - это функция).

Вот код, который я написал:

dot (x:xs) = do print x
      return xs

Компилятор выдает следующее сообщение об ошибке:

forth.hs:12:1:
Couldn't match expected type `[a]' against inferred type `IO [a]'
  Expected type: ([Char], [a] -> [a])
  Inferred type: ([Char], [a] -> IO [a])
In the expression: (".", dot)

Я подозреваю, что вызов функции print в функции dot является причиной того, что выводимый тип будет IO [a]. Есть ли способ, которым я могу игнорировать возвращаемый тип печати, так как все, что мне нужно вернуть, - это конец списка, который передается в точку.

Заранее спасибо.

Ответы [ 3 ]

8 голосов
/ 30 октября 2010

Нет, либо ваша функция вызывает побочные эффекты (так называемый IO, в данном случае печать на экране), либо нет.print выполняет IO и поэтому возвращает что-то в IO, и это не может быть отменено.

И было бы плохо, если бы компилятор мог обманом забыть о IO.Например, если ваша функция [Integer] -> [Integer] вызывается в вашей программе несколько раз с одними и теми же параметрами (как, например, []), компилятор вполне может просто выполнить функцию только один раз и использовать ее результат во всех местах, гдефункция получила "вызов".Ваш «скрытый» отпечаток будет выполнен только один раз, даже если вы вызывали функцию в нескольких местах.

Но система типов защищает вас и гарантирует, что все функции, которые используют IO, даже если только косвенно, имеютIO тип, чтобы отразить это.Если вам нужна чистая функция, вы не можете использовать в ней print.

8 голосов
/ 30 октября 2010

В большинстве функциональных языков это будет работать.Тем не менее, Haskell - это pure функциональный язык.Вам не разрешено выполнять IO в функциях, поэтому функция может быть

  1. [Int] -> [Int] без выполнения IO или
  2. [Int] -> IO [Int] с IO

Тип dot, определяемый компилятором, равен dot :: (Show t) => [t] -> IO [t], но вы можете объявить его [Int] -> IO [Int]:

dot :: [Int] -> IO [Int]

См. IO monad: http://book.realworldhaskell.org/read/io.html


Я не упомянул System.IO.Unsafe.unsafePerformIO, который следует использовать с большой осторожностью и с четким пониманием его последствий.

6 голосов
/ 30 октября 2010

Как вы, возможно, уже знаете, Haskell - это «чистый» функциональный язык программирования.По этой причине побочные эффекты (например, печать значения на экране) не являются случайными, поскольку они встречаются в более распространенных языках.Этот факт дает Хаскеллу много хороших свойств, но вы были бы прощены за то, что не позаботились об этом, когда все, что вы делаете, - это пытаетесь распечатать значение на экране.

Поскольку у языка нет прямой возможности вызывать сторону-эффекты, стратегия заключается в том, что функции могут создавать одно или несколько значений "IO action".Действие ввода-вывода заключает в себе некоторый побочный эффект (печать на консоль, запись в файл и т. Д.) И, возможно, создание значения.Ваша dot функция производит именно такое действие.Теперь у вас проблема в том, что вам нужно что-то, что может вызвать побочный эффект ввода-вывода, а также развернуть значение и, возможно, передать его обратно в вашу программу.

Без использования хаков это означает, чточто вам нужно, чтобы ваши действия ввода-вывода вернулись к функции main.На практике это означает, что все, что находится между main и dot, должно быть в «IO Monad».То, что происходит в «IO Monad», остается, так сказать, в «IO Monad».

EDIT

Вот самый простой пример, который я мог бы представить для использования вашего *Функция 1015 * в допустимой программе на Haskell:

module Main where

main :: IO ()
main =
    do
        let xs = [2,3,4]
        xr <- dot xs
        xrr <- dot xr
        return ()

dot (x:xs) =
    do
        print x
        return xs
...