Haskell writeFile - PullRequest
       8

Haskell writeFile

0 голосов
/ 12 сентября 2018

Начинающий. Получил модуль с именем HHtml, выводящий это:

  setDoc = "<!DOCTYPE = <html><head>"
  setTitle = "<title>" ++ htmlTitle generator ++ "</title>"
  setHeader = "<header>" ++ htmlHeader generator ++ "</header>"
  setMeta = "<meta>" ++ htmlMeta generator ++ "</meta></head>"
  setBody = "<body>" ++ htmlBody generator ++ "</body>"
  setFooter = "<footer>" ++ htmlFooter generator ++ "</footer>"
  setEOF = "</html>"

  setHTML = [setDoc, setTitle, setHeader, setMeta, setBody, setFooter, setEOF]

основной файл:

import HHtml
import System.IO

main = do
  let content = mapM_ putStrLn setHTML
  writeFile "index.html" content

Теперь, хотя я смотрю на это, я продолжаю получать Couldn't match type IO() with [Char] или любой другой вариант по этому вопросу. Я понимаю сообщение об ошибке, но я так растерялся из-за его исправления. Спасибо за указатели!

1 Ответ

0 голосов
/ 12 сентября 2018

mapM_ putStrLn setHTML - это действие типа IO (), которое вы назначаете имени content с помощью оператора let. При выполнении этого действия печатает каждую строку setHTML, ничего не возвращая. Вы можете выполнить это действие, написав что-то вроде этого:

main = do
  let content = mapM_ putStrLn setHTML
  content

Без переменной это просто:

main = mapM_ putStrLn setHTML

Но content является непрозрачным значением - единственное, что вы можете с ним сделать, это выполнить его из main, присоединить его к другим IO действиям с помощью >>= (или do нотации) и сохраните его в структуре данных (которая здесь не нужна). В частности, он не «хранит» содержимое страницы, он просто описывает среде выполнения, как должен печатать этот контент. И в любом случае вы заметили несоответствие типов: writeFile принимает String, a.k.a. [Char], что, очевидно, не является IO ().

Но так как вы, очевидно, хотите использовать writeFile для записи каждой строки setHTML в файл, вместо стандартного вывода, вам не нужно действие, которое будет печатать строки - вам нужны сами строки, соединенные вместе с новыми строками. Есть несколько возможных способов сделать это, в зависимости от того, как вы хотите расширить этот код.

Одним из способов является использование функции unlines :: [String] -> String для объединения строк вместе с символами новой строки, а затем использование writeFile для записи результирующих String в "index.html":

main = writeFile "index.html" (unlines setHTML)

Если вы хотите поместить составное содержимое в переменную, вы, конечно, можете сделать это:

main = do
  let content = unlines setHTML
  writeFile "index.html" content

(Действительно, вы можете перевести вызов unlines в определение setHTML, если вам не нужно setHTML, чтобы быть [String].)

Теперь writeFile примет content, потому что это значение String, а не IO (). Это хороший подход, поскольку он сохраняет логику построения страницы в чистоте и использует IO только так, как нужно для записи страницы.

В качестве альтернативы вы можете использовать более настоятельный подход, оставаясь в IO. Тогда хорошей функцией для использования будет withFile (из System.IO), которая имеет следующий тип:

FilePath -> IOMode -> (Handle -> IO r) -> IO r

Для открытия требуется FilePath, IOMode (например, ReadMode или WriteMode), чтобы указать, будете ли вы читать или писать на дескриптор, и функция который принимает дескриптор и выполняет некоторое IO и возвращает результат некоторого типа r; он возвращает действие IO, которое открывает файл, запускает вашу функцию, автоматически гарантирует, что файл закрыт (даже если возникло исключение), и возвращает результат.

Тогда вы будете использовать mapM_ аналогично тому, как у вас уже есть, чтобы печатать каждую строку для этого дескриптора - для этого есть hPutStrLn :: Handle -> String -> IO (), который пишет в определенный дескриптор, вместо putStrLn, который пишет в стандартный вывод. Компактная версия:

main = withFile "index.html" WriteMode $ \file -> do
  mapM_ (hPutStrLn file) setHTML

Или более подробная версия, если вам не нравится внешний вид лямбды:

main = withFile "index.html" WriteMode writeContents
  where writeContents file = mapM_ (hPutStrLn file) setHTML
...