I have a simple polymorphic datatype Foo
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
data Foo c =
Foo {
_bar :: c,
_baz :: c,
_quux :: c
}
makeLenses ''Foo
The generated lenses are, of course, polymorphic in c
. The type from GHCi is:
*Main> :t bar
bar :: Functor f => (c0 -> f c0) -> Foo c0 -> f (Foo c0)
I made a datatype Blah
to wrap around the lens. This works fine in simple cases (with the RankNTypes
extension, of course):
data Blah = Blah (forall c . Lens' (Foo c) c)
orange :: Blah
orange = Blah bar
But anything slightly more complicated doesn't work, for example
cheese :: [Blah]
cheese = map Blah [bar]
This last piece of code gives an error from GHC:
Couldn't match type ‘(c0 -> f0 c0) -> Foo c0 -> f0 (Foo c0)’
with ‘forall c (f :: * -> *).
Functor f =>
(c -> f c) -> Foo c -> f (Foo c)’
Expected type: ((c0 -> f0 c0) -> Foo c0 -> f0 (Foo c0)) -> Blah
Actual type: (forall c. Lens' (Foo c) c) -> Blah
In the first argument of ‘map’, namely ‘Blah’
In the expression: map Blah [bar]
It seems like the forall c f .
has disappeared from ‘(c0 -> f0 c0) -> Foo c0 -> f0 (Foo c0)’
but I don't understand why.
Why does this not compile, and what can I do to get something like this to work?
Blah
. If you move theforall c.
outside of the constructor asdata Blah = forall c. Blah (Lens' (Foo c) c)
thenmap Blah
will typecheck, butmap Blah [bar]
won't. If you expand theLens'
type and manually dodata Blah = forall c f. Functor f => Blah ((c -> f c) -> Foo c -> f (Foo c))
then you have:t map Blah
asFunctor f => [(c -> f c) -> Foo c -> f (Foo c)] -> [Blah]
and:t [bar, baz]
as[(c -> f c) -> Foo c -> f (Foo c)]
... – bheklilrFunctor
constraint inmap Blah [bar, baz]
. I believe this is because theBlah
constructor is hiding too much from the type checker (essentially), so it can't figure out how to incorporate theFunctor
constraint. If the lens type were unconstrained then it might actually work. – bheklilrBlah
asdata Blah f c where Blah :: Functor f => ((c -> f c) -> Foo c -> f (Foo c)) -> Blah f c
, then you can domap Blah [bar, baz]
and have the typeFunctor f => [Blah f c]
, but this doesn't mean that there isn't a way to hide those type variables. The important one here isf
I think, because theFunctor
constraint. The compiler has to pick an instance forFunctor
, but has no way of picking just one, so it fails the type check. – bheklilr[bar]
to have type[forall c . Lens' (Foo c) c]
, but it actually has the typeFunctor f => [(c0 -> f c0) -> Foo c0 -> f (Foo c0)]
. You can force[bar]
to have the former type, by simply specifying that type signature - but you need to enable ImpredicativeTypes. The same goes formap Blah
- it also has an impredictive type, so you will also need to specify it manually. For example;bar' :: [forall c . Lens' (Foo c) c]; bar' = [bar] ; mapBlah :: [forall c . Lens' (Foo c) c] -> [Blah]; mapBlah = map Blah
typechecks, and so willmapBlah bar'
. – user2407038