This works:
data Wrapped a = Wrapped a
alpha :: IO s -> IO ()
alpha x = do
rv <- wrapit x
return ()
where
wrapit :: IO s -> IO (Wrapped s)
wrapit x' = do
a <- x'
return (Wrapped a)
This doesn’t:
data Wrapped a = Wrapped a
alpha :: IO s -> IO ()
alpha x = do
rv <- wrapit
return ()
where
wrapit :: IO (Wrapped s)
wrapit = do
a <- x
return (Wrapped a)
Why?
This is due to the way in which type variables are scoped and quantified in standard Haskell. You can make the second version work like so:
There are two changes: the RankNTypes and ScopedTypeVariables language extensions are enabled and the explicit
forall sis added in the type signature ofalpha. The first of the two extensions is what allows us to introduce the explicitforall sthus bringing thesinto scope inside the body ofalpha, whereas the second makes it so that the signature onwrapitis not taken by the type inference engine to contain an implicitforall s— instead, thesin that signature is taken to name a type variable which should be in scope and is identified with it.The current default situation in Haskell, if I understand correctly, is that all rigid type variables (meaning type variables occurring in type signatures explicitly provided by the programmer) are implicitly quantified and not lexically scoped, so that there is no way to refer to a rigid type variable from an outer scope in an explicit signature provided in an inner scope… (Oh bother, I’m sure someone could phrase it better than this.) Anyway, from the type checker’s point of view, the
sinalpha‘s signature and the one inwrapit‘s signature are totally unrelated and cannot be unified — thus the error.See this page from the GHC docs and this page from Haskell Prime wiki for more information.
Update: I just realised that I never explained why the first version works. For the sake of completeness: note that with the first version, you could use
tin place ofsinwrapit‘s signature and nothing would change. You could even takewrapitout of thewhereblock and make it a separate top level function. The key point is that it is a polymorphic function, so that the type ofwrapit xis determined by the type ofx. No relation of the type variable used in the first versionwrapit‘s signature to that used inalpha‘s signature would be of any use here. With the second version this is of course different and you have to resort to the above mentioned trickery to makewrapit‘ssbe the same thing asalpha‘ss.