Ошибка типа в программе на Haskell - PullRequest
2 голосов
/ 19 апреля 2011

Пользователь может указать идентификатор, ширину, высоту и описание прямоугольника, а затем я записываю его в файл.Теперь я хотел бы загрузить это содержимое из файла в мою программу, но у меня есть ошибка:

Не удалось сопоставить ожидаемый тип [RectangleType] с предполагаемым типом IO [Rectangletype].В первом аргументе menuRectangles, а именно дБ.В выражении menuRectangles db.В меню выражения doRectangles db.

Что происходит?Это содержимое моего файла: [Rectangle 2 5 6 "abcabc", Rectangle 1 2 4 "abcabc"] *

Это код:

import IO
import Char
import System.Exit
import Maybe


data RectangleType = Rectangle Int Int Int deriving(Show, Read)


loadFile :: FilePath -> IO [RectangleType]
loadFile fname =
    catch (do fileContent <- readFile fname
              return (read fileContent)
    ) errorHandler
    where
        errorHandler e = do putStrLn ("Error file")
                            exitFailure

db = loadFile "db.txt"


main = do
    putStrLn "Choose option:"
    n <- getLine
    case n of
        "1"         -> do menuRectangles db; main
        "2"         -> putStrLn "bye, bye"
        otherwise   -> do putStrLn "Bad option"; main


menuRectangles :: [RectangleType] -> IO [RectangleType]
menuRectangles rs = do
    putStrLn "Please choose option:"
    putStrLn "1 - Add rectangle"
    putStrLn "2 - Show rectangle"
    putStrLn "3 - Quit"
    putStr "Number: "
    n <- getLine
    case n of
        "1"         ->  do { {- rs_new <- addRectangle rs; -} menuRectangles rs };
        "2"         ->  do { {- showRectangle rs; -} menuRectangles rs }
        "3"         ->  do { putStrLn "Quitting"; return rs }
        otherwise   ->  do { putStrLn "The End"; return rs }

EDIT: правильный код:

import IO
import Char
import System.Exit
import Maybe


data RectangleType = Rectangle Int Int Int deriving(Show, Read)


loadFile :: FilePath -> IO [RectangleType]
loadFile fname =
    catch (do fileContent <- readFile fname
              return (read fileContent)
    ) errorHandler
    where
        errorHandler e = do putStrLn ("Error file")
                            exitFailure


main = do
    db <- loadFile "db.txt"
    mainMenu db


mainMenu rs = do
    putStrLn "Choose option:"
    n <- getLine
    case n of
        "1"         -> do menuRectangles rs; mainMenu rs
        "2"         -> putStrLn "bye, bye"
        otherwise   -> do putStrLn "Bad option"; mainMenu rs


menuRectangles :: [RectangleType] -> IO [RectangleType]
menuRectangles rs = do
    putStrLn "Please choose option:"
    putStrLn "1 - Add rectangle"
    putStrLn "2 - Show rectangle"
    putStrLn "3 - Quit"
    putStr "Number: "
    n <- getLine
    case n of
        "1"         ->  do { {- rs_new <- addRectangle rs; -} menuRectangles rs };
        "2"         ->  do { {- showRectangle rs; -} menuRectangles rs }
        "3"         ->  do { putStrLn "Quitting"; return rs }
        otherwise   ->  do { putStrLn "The End"; return rs }

1 Ответ

5 голосов
/ 19 апреля 2011

В Haskell существует концепция, называемая чистым кодом. Чистый код не имеет ничего из следующего: вводимые пользователем значения, системные вызовы, генерация псевдослучайных чисел, вызовы нечистого кода и т. Д.

Гарантируется, что чистые функции всегда всегда будут иметь одинаковое поведение в зависимости от лексического содержания вашей программы (например, они могут возвращать разные значения, но причина, по которой они возвращают разные значения, не может зависеть от "мира").

Это очень мощная политика в функциональных языках, которая допускает очень мощный код. Например, вы знаете, что вызов функции не изменит какую-либо несвязанную глобальную переменную или состояние какой-либо другой структуры данных. Я часто применяю эту политику к коду Python.

То, как Хаскелл навязывает чистоту-примесь, - это монада IO.

Все, что касается "мира", заключено в монаду IO, которая представляла, что значения были "испорчены". Если что-либо касается этих «испорченных» значений, возвращаемые ими значения также должны быть испорчены.

Вам нужно будет запустить чистый код в монаде ввода-вывода, если вы хотите, чтобы он имел доступ к этим значениям. Оказавшись внутри монады ввода-вывода, вы можете «развернуть» прочитанное вами значение и передать его в ваш чистый код, затем ваш чистый код возвращает значение, а затем вы можете напечатать свое значение. Все работает как положено, но вы должны сделать это в монаде IO.

Обратите внимание, что хорошо бы убедиться, что большая часть вашего кода написана в чистой, функциональной форме, кроме монады ввода-вывода. например чистая doStuffWithRectangles функция, которая затем вызывается из монады IO.

Прекрасно то, что вашему чистому коду не нужно знать, что значение Rectangle было испорчено типом IO Rectangle. Пока вы работаете в монаде IO, ваш чистый код будет думать, что это просто обычный Rectangle.


И просто, чтобы сделать этот ответ более явным: readFile возвращает вещи, завернутые в монаду ввода / вывода, потому что эти вещи происходят из "мира" (файловой системы) и, например, если вы изменили файл во время выполнения программы, он может вернуть другое значение.

--db = loadFile "db.txt" REMOVED--

main = do --within the IO monad
    putStrLn "Choose option:"
    n <- getLine
    **DB <- LOADFILE "db.txt"** --db is now a pure value
    case n of
        "1"         -> do menuRectangles db; main
        "2"         -> putStrLn "bye, bye"
        otherwise   -> do putStrLn "Bad option"; main

Дополнительная информация: http://www.haskell.org/tutorial/io.html

Хорошее онлайн / прочтение: http://learnyouahaskell.com/

Также хорошо читать онлайн / оффлайн: http://book.realworldhaskell.org/

...