Ограничение переменной без типа

Я пытаюсь использовать библиотеку Parsec для анализа списка значений Token. Я хочу использовать функцию token в тексте .Parsec.Prim для соответствия одному значению. Кажется, это должно работать:

type TokenParser a = Parsec [Token] () a

mytoken :: (Token -> Bool) -> TokenParser Token
mytoken test = token showTok posFromTok testTok
  where -- and so on

Это дает ошибку компиляции:

No instance for (Stream [Token] Identity Token)
  arising from a use of `Prim.token'
Possible fix:
  add an instance declaration for (Stream [Token] Identity Token)

Хорошо, давайте изменим объявление типа на mytoken:

mytoken :: Stream [Token] Identity Token => (Token -> Bool) -> TokenParser Token

Это работает после того, как мы добавим расширение {-# LANGUAGE FlexibleContexts #-}.

Что здесь происходит? Прежде всего, определение класса Stream в Text. Parsec.Prim имеет Monad m => Stream [tok] m tok в качестве одного из экземпляров. Разве Stream [Token] Identity Token уже не должно быть охвачено этим экземпляром? Во-вторых, как получается, что это вообще что-то ограничивает? В типе mytoken нет переменных типа, которые должны быть ограничены.

Что еще хуже, когда я собираюсь использовать свою новую «ограниченную» mytoken в другой функции, я получаю точно такую ​​же ошибку, No instance for (Stream [Token] Identity Token) arising from... На самом деле это требует наложения того же, по-видимому, ограничения типа no-op для типа функции, пытающейся вызвать mytoken .

Если кто-нибудь может помочь объяснить мне, что делает это ограничение типа, я был бы очень признателен.


person Cardano    schedule 04.04.2014    source источник


Ответы (1)


Следующий файл является минимальным, который я мог бы создать, который воспроизводит вашу проблему. (В будущем вы сами должны создать для нас такой файл.)

import Text.Parsec.Prim

data Token

type TokenParser a = Parsec [Token] () a

mytoken :: (Token -> Bool) -> TokenParser Token
mytoken test = token showTok posFromTok testTok

showTok = undefined
posFromTok = undefined
testTok = undefined

Изменение импорта с Text.Parsec.Prim на Text.Parsec устраняет ошибку. (Соответствующий экземпляр определен в Text.Parsec.String, который импортируется Text.Parsec.Prim.) Вы также спрашиваете, почему это изменение заставляет его компилироваться, но не работает:

mytoken :: Stream [Token] Identity Token => (Token -> Bool) -> TokenParser Token

Общее правило состоит в том, что тип «C => T» говорит: «вы можете использовать эту вещь, как если бы это была T, при условии, что вы можете показать, что ограничение C удовлетворяется». Таким образом, предоставление mytoken типа, который вы сделали, говорит о том, что нужно подождать, пока mytoken не будет использоваться, как если бы это был простой старый (Token -> Bool) -> TokenParser Token, и как только это произойдет, попытайтесь выполнить обязательство показать, что Stream [Token] Identity Token выполняется. Поскольку вы все еще не импортировали соответствующий экземпляр, он не может выполнить это обязательство и жалуется.

person Daniel Wagner    schedule 04.04.2014
comment
Потрясающий! Это сработало. В будущем я обязательно включу минимально воспроизводимый файл. Есть ли у вас понимание того, что делает ограничение типа в случае, если вы не можете импортировать соответствующий экземпляр? - person Cardano; 04.04.2014
comment
@Кардано Да, конечно. Я добавлю предложение об этом. - person Daniel Wagner; 04.04.2014
comment
Обратите внимание, что этот экземпляр недавно был деорфанизирован, так что это не должно быть проблемой при запуске. из следующего выпуска. - person Roman Cheplyaka; 04.04.2014