I'm trying to refactor my function by giving it a lens argument (from the xml-lens
package). I'm missing something about type quantifiers. What is going on here?
*Main> let z name = listToMaybe $ pom ^.. root ./ ell name . text
*Main> :t z
z :: Text -> Maybe Text
*Main> let z name l = listToMaybe $ pom ^.. l ./ ell name . text
<interactive>:13:38:
Couldn't match expected type ‘(Element -> f Element)
-> Document -> f Document’
with actual type ‘t’
because type variable ‘f’ would escape its scope
This (rigid, skolem) type variable is bound by
a type expected by the context:
Applicative f => (Element -> f Element) -> Document -> f Document
at <interactive>:13:38-57
Relevant bindings include
l :: t (bound at <interactive>:13:12)
z :: Text -> t -> Maybe Text (bound at <interactive>:13:5)
In the first argument of ‘(./)’, namely ‘l’
In the second argument of ‘(^..)’, namely ‘l ./ ell name . text’
What is interesting, this signature works.
textFrom :: Text -> Document -> Lens' Document Element -> Maybe Text
textFrom name pom ln = listToMaybe $ pom ^.. ln ./ ell name . text
The problem here isn't with lenses or
xml-lens
directly. It's a higher-rank type inference issue.Simplified test case
First let's make a minimal-ish example using the problematic type from your question. In your code, you're passing
l
to the function(./)
, which expects aTraversable
; I'm replacing(./)
withg
and leaving out the rest of the function.Error:
Uncommenting the type signature fixes it, as with your problem.
Let's expand the type signature to see why.
The punchline here is simply that
f
has a higher-rank type, i.e. it contains a nestedforall
; you needRankNTypes
to write eitherf
org
.Inferring higher-rank types
Type inference for higher-rank types is not always possible. Your problem boils down to "GHC can't infer this higher-rank type"; the answer to that is basically "GHC makes no promise that it can do so."
Specifically, the one documented assumption GHC makes regarding inference and higher-rank types is this, from the GHC 7.8.3 docs:
In our example, the variable
l
is lambda-bound, and it doesn't have an explicit polymorphic type. Therefore GHC assumes that its type (which the error message callst
) has no foralls. Trying to unify it withforall f. (a0 -> f b0) -> s0 -> f t0
violates that assumption.The bit about type variable
f
escaping its scope indicates thatf
needs to have a forall on it.By the way, the real minimal example is this: