2013-08-13 2 views
1

Для удовольствия Я создаю библиотеку синтаксического анализатора. В этой библиотеке у меня есть тип с Parser данных:Тип Ограничения в Typeclass

data Parser e a = Parser (String -> Either e (a, String)) 

Я могу определить Functor и Applicative экземпляров Parser, но я не думаю, что я могу сделать Alternative экземпляра без ограничения типа «ошибки» или тип «значения», который может вернуть парсер. Первоначально это заставило меня создать экземпляр Applicative для тех случаев, когда типы ошибок являются сообщениями String, но я понял, что должен уметь освободить это ограничение для любого типа данных сообщений, который имеет экземпляр (или возможно Monoid вместо?). Имея это в виду, я написал следующее:

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
instance Alternative e => Alternative (Parser e) 
    where 
    empty = Parser $ \s -> Left empty 
    (<|>) (Parser p1) (Parser p2) = Parser $ \s -> tryParser s p2 $ p1 s 
     where 
     tryParser s p2 (Left _) = p2 s 
     tryParser _ _ x   = x 

К сожалению, это не скомпилировано. Когда я загрузить его в GHCI я получаю сообщение об ошибке:

Parsertest.hs:31:47: 
    Expecting one more argument to `e' 
    In the instance declaration for `Alternative (Parser e)' 
Failed, modules loaded: none. 

Когда я поиск в Интернете, это, кажется, решение, но это не работает для меня. Что мне не хватает?

ответ

6

Проблема заключается в том, что Alternative для типа конструкторов любезного * -> *, так что если вы говорите

instance Alternative e => .... 

затем e должна быть типа конструктором, а не типа. Таким образом, e может быть [] или Maybe или что-то еще, но не Int.

Оператор Alternative - <|>, который имеет тип Alternative e => e a -> e a -> e a. Это заставляет e принять аргумент для создания типа, например Maybe должен принять аргумент, например Maybe Int.

Используйте Monoid вместо Alternative, потому что это класс типа вместо класса конструктора. Оператор mappend имеет тип Monoid e => e -> e -> e, который вы хотите для объединения ошибок.

+0

Это заставляет вещи скомпилироваться снова. Знаете ли вы, где я могу читать, когда Monoid, Alternative, MonadPlus имеет смысл использовать? – chanko08

+1

Wellllll, я написал [ответ] (http://stackoverflow.com/a/13115196/1598537) и [другой] (http://stackoverflow.com/a/13174738/1598537) по аналогичной теме ([здесь ] (http://stackoverflow.com/questions/13080606)), если вы довольны довольно теоретическими обсуждениями там. Если вы хотите получить более практические советы после прочтения их, вы можете попробовать задать это как новый вопрос, но будьте осторожны, чтобы было ясно, что вы их читали, и что вам нужно руководство для того, когда выбирать их, или если это незначительное разъяснение одного из этих ответов, спросите в комментарии. – AndrewC

+0

@chanko Я должен был сказать, что Alternative _is_ подходит для парсера, в то время как Monoid имеет смысл для типа ошибки. – AndrewC

Смежные вопросы