"Незаконный полиморфный или квалифицированный тип" в 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, но я не вижу ни одного из этих вопросов, применимых к моей нынешней ситуации.

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

Ответ 1

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

(forall a. foo) -> bar

Что вы можете сделать с помощью

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

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

Ответ 2

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

Вы можете использовать Lens в сигнатуре типа, даже не имея RankNTypes on. Это typechecks:

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. Это typechecks:

{-# LANGUAGE ExplicitForAll #-}

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

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

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

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