Хаскель напиши String, легкий вопрос - PullRequest
2 голосов
/ 18 января 2011
writeStr []=putChar ' '         
writeStr (x:xs) = (putChar x)(writeStr xs)

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

Ответы [ 2 ]

4 голосов
/ 18 января 2011

Ваш код немного странный. Если я правильно понял, вы пытаетесь напечатать строку. Ваш метод состоит в том, чтобы поместить первую строку, а затем вторую. Но в Haskell невозможно объединить два действия ввода-вывода следующим образом. Посмотрите снова в этом уроке об этом, вот как это может выглядеть:

writeStr []     = return () -- you had putChar ' ',
writeStr (x:xs) = do putChar x -- but this would print a superfluous whtiespace
                     writeStr xs

Если вы хотите сделать несколько вещей последовательно, используйте либо ключевое слово do, либо монадные комбинаторы. Это очень просто, вот так:

do action1
   action2
   action3
   ...
3 голосов
/ 20 января 2011

FUZxxl ответил на немедленный вопрос, но я хотел бы расширить его еще несколькими способами написания «writeStr», чтобы проиллюстрировать больше о монадах.

Как сказал Делнан в комментариях, вы также можете написать

writeStr [] = return ()
writeStr (x:xs) = putChar x >> writeStr xs

На самом деле это десагаратная версия нотации do. Оператор «>>» используется для последовательного монадического действия. На самом деле это специализированная версия оператора "bind", написанная ">> =". См. этот вопрос для более подробной информации.

Но когда вы смотрите на это, кажется, что все, что мы делаем, это применяем "putChar" к каждому элементу в списке аргументов. Для этого в Prelude уже есть функция map, поэтому мы можем написать:

writeStr xs = map putChar xs

Но когда ты попробуешь, это не сработает. Причина становится очевидной, если вы зайдете в GHCi и напечатаете это:

:type map putChar "Hello"
[IO ()]

Вы хотите одно действие "IO ()", но это дает вам их список. Вам нужна функция, которая превращает этот список действий ввода-вывода в одно действие ввода-вывода. К счастью, один существует. Прелюдия содержит эти две функции

sequence :: [IO a] -> IO [a]
sequence_ :: [IO a] -> IO ()

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

Итак, теперь вы можете написать:

writeStr xs = sequence_ $ map putChar xs

Но есть способ сократить это. Напомним "." оператор, который связывает две функции вместе, и способ, которым Haskell «каррирует» аргументы функции? Мы можем переписать функцию выше как:

writeStr = sequence_ . map putChar

Этот стиль "без очков" поначалу выглядит и кажется очень странным; это делает «writeStr» больше похожим на константу, чем на функцию. Но это избавляет от необходимости отслеживать имена переменных вокруг кода, когда вы читаете его, и поэтому часто предпочитается. Это также намного короче и более читабельно, когда вы помещаете что-то сложное в качестве аргумента в «map» или аналогичные функции более высокого порядка.

Но мы можем пойти еще короче. Шаблон «sequence.map f» очень распространен, поэтому модуль «Control.Monad» определяет еще пару функций для его воплощения:

mapM :: (a -> IO b) -> [a] -> IO [b]
mapM f = sequence . map f

mapM_ :: (a -> IO b) -> [a] -> IO ()
mapM_ f = sequence_ . map f

Таким образом, вы можете наконец написать

writeStr = mapM_ putChar
...