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

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

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()].

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


person guthrie    schedule 20.04.2011    source источник


Ответы (4)


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

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

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

prints :: [t] -> [a]

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

person evnu    schedule 20.04.2011
comment
Спасибо имеет смысл, но затем просто возвращает: code Не может соответствовать ожидаемому типу IO ()' with actual type [a0]' В первом аргументе return', namely []' В выражении: return [] В уравнении для `prints': prints [] = return [] - person guthrie; 21.04.2011
comment
Ты прав. return (px:sep:rest) относится к типу IO [()] (удалите сигнатуру типа и шаблон print [] = [], затем загрузите его в ghci, чтобы попробовать. См. тип, используя :i prints). Поэтому тип prints должен быть prints :: [Value] -> IO [()]. При этом использование print [] = return [] работает. - person evnu; 21.04.2011

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

prints  []    = return []

Однако ваши отпечатки очень странные. Он возвращает [()], потому что 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
person Don Stewart    schedule 20.04.2011

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

prints (x:xs) = printv x : print ", " : prints xs
person kennytm    schedule 20.04.2011

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

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

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

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

Тем не менее, я действительно рекомендовал бы, чтобы вместо выполнения всей работы в функции ввода-вывода вы написали чистую функцию для отображения нужного вам текстового формата, а затем просто распечатали ее. В частности, кажется, что экземпляр Show для 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`.
person Austin Taylor    schedule 20.04.2011
comment
Спасибо. Я переделаю - я просто экспериментировал со списком действий, как в некоторых ранних примерах в SOE. Пытаюсь заработать на IO и делать блоки. После того, как я разобрался со всем этим, мне больше нравится ваше использование Show! Тем не менее, я хотел бы выяснить, почему мой исходный код не работает, даже если он не так оптимален. - person guthrie; 21.04.2011