Why does this typecheck:
runST $ return $ True
While the following does not:
runST . return $ True
GHCI complains:
Couldn't match expected type `forall s. ST s c0' with actual type `m0 a0' Expected type: a0 -> forall s. ST s c0 Actual type: a0 -> m0 a0 In the second argument of `(.)', namely `return' In the expression: runST . return
The composition of two functions g and f is the new function we get by performing f first, and then performing g. For example, if we let f be the function given by f(x) = x2 and let g be the function given by g(x) = x + 3, then the composition of g with f is called gf and is worked out as gf(x) = g(f(x)) .
To evaluate a composite function f(g(x)) at some x = a, first compute g(a) by substituting x = a in the function g(x). Then substitute g(a) into the function f(x) by substituting x = g(a). In the same way, we can calculate g(f(a)) as well.
The notation for function composition is h = f • g or h(x) = (f • g)(x) and is read as 'f of g of x'. The procedure is called composition because the new function is composed of the two given functions f and g, where one function is substituted into the other.
Symbol. The symbol for composition is a small circle: (g º f)(x) It is not a filled in dot: (g · f)(x), as that means multiply.
The short answer is that type inference doesn't always work with higher-rank types. In this case, it is unable to infer the type of (.)
, but it type checks if we add an explicit type annotation:
> :m + Control.Monad.ST > :set -XRankNTypes > :t (((.) :: ((forall s0. ST s0 a) -> a) -> (a -> forall s1. ST s1 a) -> a -> a) runST return) $ True (((.) :: ((forall s0. ST s0 a) -> a) -> (a -> forall s1. ST s1 a) -> a -> a) runST return) $ True :: Bool
The same problem also happens with your first example, if we replace ($)
with our own version:
> let app f x = f x > :t runST `app` (return `app` True) <interactive>:1:14: Couldn't match expected type `forall s. ST s t0' with actual type `m0 t10' Expected type: t10 -> forall s. ST s t0 Actual type: t10 -> m0 t10 In the first argument of `app', namely `return' In the second argument of `app', namely `(return `app` True)'
Again, this can be solved by adding type annotations:
> :t (app :: ((forall s0. ST s0 a) -> a) -> (forall s1. ST s1 a) -> a) runST (return `app` True) (app :: ((forall s0. ST s0 a) -> a) -> (forall s1. ST s1 a) -> a) runST (return `app` True) :: Bool
What is happening here is that there is a special typing rule in GHC 7 which only applies to the standard ($)
operator. Simon Peyton-Jones explains this behavior in a reply on the GHC users mailing list:
This is a motivating example for type inference that can deal with impredicative types. Consider the type of
($)
:($) :: forall p q. (p -> q) -> p -> q
In the example we need to instantiate
p
with(forall s. ST s a)
, and that's what impredicative polymorphism means: instantiating a type variable with a polymorphic type.Sadly, I know of no system of reasonable complexity that can typecheck [this] unaided. There are plenty of complicated systems, and I have been a co-author on papers on at least two, but they are all Too Jolly Complicated to live in GHC. We did have an implementation of boxy types, but I took it out when implementing the new typechecker. Nobody understood it.
However, people so often write
runST $ do ...
that in GHC 7 I implemented a special typing rule, just for infix uses of
($)
. Just think of(f $ x)
as a new syntactic form, with the obvious typing rule, and away you go.
Your second example fails because there is no such rule for (.)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With