Предварительные примечания: Учитывая представленные здесь доказательства, я предполагаю, что вы используете:
type GenericQ r = forall a . Data a => a -> r
из syb и gmapQ :: Data a => (forall d. Data d => d -> u) -> a -> [u]
от Data.Data
.
Пожалуйста, дайте мне знать, если я ошибаюсь по этому поводу.Кроме того, любые forall
s в дальнейшем будут записаны явно.
Здесь есть нечто большее, чем кажется на первый взгляд. Как предполагает Ли-Яо Ся , речь идет об обобщении типа op
.Есть три важных факта о вашем первом определении shallowest
:
До обобщения , предполагаемый тип op
равен Data d => d -> f a
.Учитывая ограничение Data d
, правило 1 ограничения мономорфизма (см. подраздел 4.5.5 отчета ) означает, что d
в этом типе не может быть обобщено.
В теле shallowest
, op
обнаруживается в двух местах.Первый - op z
, с z :: a1
, связанным и ограниченным на верхнем уровне подписью shallowest
.В результате, это вхождение op
не требует обобщения типа аргумента: для него тип op
может быть forall f a. a1 -> f a
, мономорфным в переменной типа a1
(я взял эту терминологию из подраздела 4.5.4 Отчета ).
Тем не менее, другим случаем является gmapQ op z
.gmapQ
имеет тип ранга 2, требующий полиморфного аргумента.Таким образом, это вхождение требует обобщения типа аргумента op
, как отмечалось в конце ответа Ли-Яо Ся.
# 1 и # 3 являются противоречивыми требованиями,и, таким образом, вы получаете ошибку типа, которую можно избежать, либо отключив ограничение мономорфизма, либо потребовав op
быть полиморфным для типа аргумента с сигнатурой.Благодаря другому вхождению op
, описанному в # 2, ситуация сообщается как несоответствие, связанное с двумя вхождениями.
Ниже приведен более минимальный расширенный пример, который может помочь увидеть, чтопродолжается.(Если вы собираетесь добавить следующие фрагменты в GHCi, кроме -XRankNTypes
, вы также должны установить -XMonomorphismRestriction
и -XNoExtendedDefaultRules
, чтобы увидеть те же результаты.)
Это функция с рангом-2 типа, который будет играть роль gmapQ
:
glub :: (forall x. Show x => x -> String) -> String
glub f = f 7
Теперь давайте попробуем сценарий, аналогичный сценарию shallowest
...
foo1 :: forall a. Show a => a -> String
foo1 x = bar x ++ glub bar
where
bar = show
...и есть ваша ошибка:
<interactive>:506:23: error:
• Couldn't match type ‘x’ with ‘a’
‘x’ is a rigid type variable bound by
a type expected by the context:
forall x. Show x => x -> String
at <interactive>:506:18-25
‘a’ is a rigid type variable bound by
the type signature for:
foo1 :: forall a. Show a => a -> String
at <interactive>:505:1-38
Expected type: x -> String
Actual type: a -> String
• In the first argument of ‘glub’, namely ‘bar’
In the second argument of ‘(++)’, namely ‘glub bar’
In the expression: bar x ++ glub bar
• Relevant bindings include
bar :: a -> String (bound at <interactive>:508:3)
x :: a (bound at <interactive>:506:5)
foo1 :: a -> String (bound at <interactive>:506:1)
Добавление символа подстановки, куда должна идти подпись bar
, дает дополнительную ошибку, которая несколько более наводит на мысль:
foo2 :: forall a. Show a => a -> String
foo2 x = bar x ++ glub bar
where
bar :: _
bar = show
• Found type wildcard ‘_’ standing for ‘a -> String’
Where: ‘a’ is a rigid type variable bound by
the type signature for:
foo2 :: forall a. Show a => a -> String
at <interactive>:511:1-38
To use the inferred type, enable PartialTypeSignatures
• In the type signature: bar :: _
In an equation for ‘foo2’:
foo2 x
= bar x ++ glub bar
where
bar :: _
bar = show
• Relevant bindings include
x :: a (bound at <interactive>:512:5)
foo2 :: a -> String (bound at <interactive>:512:1)
Примечаниекак подстановочный знак "стоит для a -> String
" указан как отдельный факт, а a
не связан с сигнатурой типа foo2
.Я считаю, что это соответствует различию между мономорфным в переменной типа и полиморфным, о котором я упоминал в пункте № 2 выше.
Если bar
подпись полиморфного типа заставит его работать:
foo3 :: forall a. Show a => a -> String
foo3 x = bar x ++ glub bar
where
bar :: forall b. Show b => b -> String
bar = show
То же самое делает определение стержня точечным, что обходит ограничение мономорфизма, делая его «привязкой функции», а не «простой привязкой шаблона» :
foo4 :: forall a. Show a => a -> String
foo4 x = bar x ++ glub bar
where
bar x = show x
Дляради полноты стоит отметить, что отсутствие ограничения на тип означает отсутствие ограничения мономорфизма:
foo5 :: forall a. Show a => a -> String
foo5 x = bar x ++ glub bar
where
bar = const "bar"
Смежная ситуация предполагает использование bar
дважды, но без функции ранга 2:
foo6 x y = bar x ++ bar y
where
bar = show
Какой тип GHC выведет для foo6
?
GHCi> :t foo6
foo6 :: Show a => a -> a -> [Char]
Аргументы получают тот же тип, поскольку в противном случае потребуется обобщение bar
, для которого требуется сигнатура типа (илиТочность и т. д.):
foo7 x y = bar x ++ bar y
where
bar :: forall a. Show a => a -> String
bar = show
GHCi> :t foo7
foo7 :: (Show a1, Show a2) => a1 -> a2 -> [Char]
Поскольку я еще не упомянул об этом, вот аналог вашего второго shallowest
:
foo8 :: forall a. Show a => a -> String
foo8 x = bar x
where
bar = show
Это стоитEMPHпри условии, что bar
здесь на самом деле не обобщается: оно мономорфно в переменной типа a
.Мы все еще можем разбить этот пример, связавшись с foo7
, а не с bar
:
foo9 = bar
where
bar :: _
bar = show
В этом случае bar
не обобщается и не foo
(теперь бессмысленно и без подписи). Это означает, что переменная мономорфного типа никогда не разрешается. В соответствии с правилом 2 ограничения мономорфизма оно становится переменной неоднозначного типа:
<interactive>:718:14: error:
• Found type wildcard ‘_’ standing for ‘a0 -> String’
Where: ‘a0’ is an ambiguous type variable
To use the inferred type, enable PartialTypeSignatures
• In the type signature: bar :: _
In an equation for ‘foo9’:
foo9
= bar
where
bar :: _
bar = show
• Relevant bindings include
foo9 :: a0 -> String (bound at <interactive>:716:5)
<interactive>:719:13: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘show’
prevents the constraint ‘(Show a0)’ from being solved.
Relevant bindings include
bar :: a0 -> String (bound at <interactive>:719:7)
foo9 :: a0 -> String (bound at <interactive>:716:5)
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instances exist:
instance Show a => Show (ZipList a)
-- Defined in ‘Control.Applicative’
instance Show Constr -- Defined in ‘Data.Data’
instance Show ConstrRep -- Defined in ‘Data.Data’
...plus 64 others
...plus 250 instances involving out-of-scope types
(use -fprint-potential-instances to see them all)
• In the expression: show
In an equation for ‘bar’: bar = show
In an equation for ‘foo9’:
foo9
= bar
where
bar :: _
bar = show
Добавление сигнатуры типа к bar
в определении foo9
не поможет - оно просто меняет точку, из которой сообщается об ошибке. Замена bar
на что-то без ограничений устраняет ошибку, поскольку позволяет обобщать как bar
, так и foo
.