"Незаконный полиморфный или квалифицированный тип" в 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 может быть полезно.