Недопустимый полиморфный или квалифицированный тип в Control.Lens

Я работаю с Control.Lens. Фактическая функция, которую я пишу, довольно сложна, но для целей этого вопроса я свел ее к минимальному ошибочному примеру:

import Control.Lens    

exampleFunc :: Lens s t a b -> String
exampleFunc _ = "Example"

Это не удается скомпилировать, что приводит к следующему сообщению об ошибке:

Illegal polymorphic or qualified type: Lens s t a b
Perhaps you intended to use -XRankNTypes or -XRank2Types
In the type signature for `exampleFunc':
  exampleFunc :: Lens s t a b -> String

Почему это незаконно? Это кажется ужасно похожим на следующее, которое действительно компилируется:

import Data.Maybe

exampleFunc' :: Maybe (s, t, a, b) -> String
exampleFunc' _ = "Example"

Итак, я предполагаю, что разница заключается в определении Lens. Но как насчет типа Lens, который делает тип exampleFunc незаконным? У меня есть подозрение, что это связано с квалификацией Functor в определении Lens, но я могу ошибаться. Для справки, определение Lens:

type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t

Итак, должен ли я как-то удовлетворять квалификации Functor в моем определении exampleFunc? Если да, то как? Я не вижу, где в моей подписи типа у меня есть возможность объявить это ограничение. Или, может быть, я на неправильном пути, и моя проблема не имеет ничего общего с ограничением Functor.

Я прочитал все вопросы о переполнении стека, которые я смог найти относительно сообщения об ошибке «незаконный полиморфный и т. Д.». Возможно, это связано с тем, что я не знаком с показом Haskell, но я не вижу ни одного из этих вопросов, применимых к моей текущей ситуации.

Я также не смог найти никакой документации о том, что означает сообщение об ошибке в целом.


person rlkw1024    schedule 20.11.2013    source источник
comment
RankNTypes — это одно из расширений, которое вы можете безопасно включать, когда компилятор предлагает это сделать. Это нормально и для многих других расширений, хотя, возможно, не для OverlappingInstances и IncoherentInstances.   -  person leftaroundabout    schedule 20.11.2013


Ответы (2)


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

(forall a. foo) -> bar

С которым вы тоже можете

{-# LANGUAGE RankNTypes #-} -- Rank2Types is a synonym for RankNTypes

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

person Daniel Gratzer    schedule 20.11.2013
comment
Это не то, что означает отрицательная позиция. Например, в (a -> r) -> r a появляется в положительной позиции. - person Ben Millwood; 07.12.2013
comment
@BenMillwood Это упрощенная версия, и да, отрицательная и положительная позиции ведут себя как умножение, в том смысле, что отрицательная позиция отрицательной позиции является положительной позицией. Но для этого случая я чувствовал, что упрощенного описания было достаточно. - person Daniel Gratzer; 07.12.2013

exampleFunc не компилируется, потому что синоним типа Lens является полиморфным и встречается в подписи в так называемой "отрицательной позиции", то есть слева от ->.

Вы можете использовать Lens в сигнатуре типа даже без включения RankNTypes. Этот тип проверяет:

import Control.Lens

lensy :: Lens' (a,b) a 
lensy = _1

Но это не позволяет проверить тип:

oops :: Lens' (a,b) a -> Int
oops = const 5 

Почему? По той же причине это также не может проверить тип без RankNTypes:

{-# LANGUAGE ExplicitForAll #-}

fails :: (forall a. a -> Int) -> Int
fails = undefined

Здесь forall находится в отрицательном положении и колеблется только над a -> Int. Это реализация fails, а не вызывающий fails, тот, кто выбирает тип a. Вызывающий должен предоставить функцию аргумента, которая работает для всех a. Эта функция требуется расширение RankNTypes.

Когда forall охватывает всю сигнатуру (например, когда Lens определяется изолированно), в RankNTypes нет необходимости. Этот тип проверяет:

{-# LANGUAGE ExplicitForAll #-}

typechecks :: forall a. (a -> Int) -> Int
typechecks = undefined

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

exampleFunc' работало, потому что, когда forall не указано, для каждой переменной существуют неявные foralls, охватывающие всю сигнатуру.

Это объяснение из списка рассылки Haskell может оказаться полезным.

person danidiaz    schedule 20.11.2013