Симпатичная печать синтаксического дерева в Haskell

#haskell #types #io #pretty-print

#haskell #типы #io #pretty-печать

Вопрос:

Я не понимаю эту ошибку типа:

 Couldn't match expected type `[t0]' with actual type `IO ()'
In the return type of a call of `printv'
In a stmt of a 'do' expression: px <- printv x
In the expression:
  do { px <- printv x;
       sep <- print ", ";
       rest <- prints xs;
       return (px : sep : rest) }
  

От:

 data    Value     = IntValue     Int
                  | TruthValue   Bool
                    deriving (Eq, Show)

printv :: Value -> IO()
printv (IntValue   i) = print i
printv (TruthValue b) = print ("boolean"    show b)

prints :: [Value] -> [IO()]
prints  []    =  []
prints (x:xs) = do px   <- printv x
                   sep  <- print ", "
                   rest <- prints xs
                   return (px:sep:rest)
  

Мне кажется, что каждый элемент ( px ) преобразуется в IO() действие, а затем добавляется к списку одинаковых объектов, создавая таким образом [IO()] список.

Чего мне здесь не хватает? Преобразование его в список строк, путем удаления печати, работает нормально.

Ответ №1:

Вы упускаете return в [] случае prints :

 prints  []    = return []
  

Однако ваши отпечатки очень странные. Он возвращает a [()] , потому что print выводит строки на консоль, а не возвращает их.

Вы имеете в виду возвращать строки из вашей printv функции?


Поскольку вы пытаетесь красиво напечатать синтаксическое дерево, вот примерно правильный способ сделать это:

Вот так:

 import Text.PrettyPrint
import Data.List

data Value 
        = VInt   Int
        | VBool  Bool
        deriving (Eq, Show)

class Pretty a where
    pretty :: a -> Doc

instance Pretty Value where
    pretty (VInt i)     = int i
    pretty (VBool b)    = text "Boolean" < > text (show b)

draw :: [Value] -> String
draw = intercalate ", " . map (render.pretty)

main = putStrLn $ draw [VInt 7, VBool True, VInt 42]
  

Запуск его:

 *A> main
7, Boolean True, 42
  

Ответ №2:

Присмотритесь повнимательнее к типу вашей функции:

 prints :: [Value] -> [IO()]
  

Но если мы теперь посмотрим на prints [] = [] , это не может совпадать, потому что тип этого

 prints :: [t] -> [a]
  

Следовательно, вы пропустили использование prints [] = return [] , чтобы заставить его работать.

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

1. Спасибо имеет смысл — но затем просто возвращает: code Не удалось сопоставить ожидаемый тип IO ()' with actual type [a0]’ В первом аргументе return', namely []’ В выражении: return [] В уравнении для `prints’: prints [] = return []

2. Вы правы. return (px:sep:rest) имеет тип IO [()] (удалите подпись типа и шаблон print [] = [] , затем загрузите его в ghci, чтобы попробовать. Посмотрите тип с помощью :i prints ). Следовательно, тип prints должен быть prints :: [Value] -> IO [()] . При этом использование print [] = return [] работает.

Ответ №3:

Если вы не оцениваете действие ввода-вывода, вам не нужен do блок. Просто обрабатывайте IO () как обычный тип.

 prints (x:xs) = printv x : print ", " : prints xs
  

Ответ №4:

Вы не хотите prints возвращать массив действий ввода-вывода. Вы хотите, чтобы оно возвращало одно действие ввода-вывода, представляющее каждое из связанных вместе действий ввода-вывода. Что-то вроде:

 prints xs = mapM_ (x -> printv x >> putStr ", ") xs
  

За исключением того, что я не думаю, что новые строки окажутся там, где вы хотите.

Посмотрите на документацию для mapM и sequence для получения дополнительной информации. В частности, реализация sequence, вероятно, похожа на то, что вы пытаетесь сделать.

Однако я бы действительно рекомендовал, чтобы вместо выполнения всей работы в функции ввода-вывода вы написали чистую функцию для отображения текстового формата, который вы хотите, а затем просто распечатали это. В частности, кажется, что экземпляр Show for Value был бы уместен.

 instance Show Value where
  show (IntValue   i) = show i
  show (TruthValue b) = "boolean "    show b
  

Таким образом, вы можете просто вызывать print value вместо printv value , и если вы действительно хотите, вы могли бы определить prints следующим образом.

 import Data.List
prints :: (Show a) => [a] -> IO ()
prints = putStrLn . intercalate ", " . map show`.
  

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

1. Спасибо. Я переделаю это — я просто экспериментировал с использованием списка действий, как в некоторых ранних примерах в SOE. Пытаюсь освоить ввод-вывод и создавать блоки. После того, как я со всем этим разобрался, мне больше нравится ваше использование Show! Тем не менее, я хотел бы выяснить, почему мой исходный код терпит неудачу, даже если он не такой оптимальный.