La respuesta corta es que la inferencia de tipo no siempre funciona con tipos de rango superior. En este caso, no es capaz de inferir el tipo de (.)
, pero el tipo comprueba si añadimos un tipo de anotación explícita:
> :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
El mismo problema ocurre con el primer ejemplo, si sustituimos ($)
con nuestra propia versión:
> 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)'
una vez más, esto puede ser resuelto mediante la adición de anotaciones de tipo:
> :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
Lo que ocurre aquí es que no hay una regla de tipado especial en GHC 7 que solo se aplica al operador estándar ($)
. Simon Peyton-Jones explica este comportamiento en a reply on the GHC users mailing list:
Este es un ejemplo motivador para la inferencia tipo que puede hacer frente a tipos impredicativas. Considere el tipo de ($)
:
($) :: forall p q. (p -> q) -> p -> q
En el ejemplo que necesitamos para crear una instancia p
con (forall s. ST s a)
, y eso es lo que significa polimorfismo impredicativo: crear instancias de una variable de tipo con un tipo polimórfico .
Tristemente, no conozco ningún sistema de complejidad razonable que pueda tipear [this] sin ayuda. Hay muchos sistemas complicados, y tengo sido coautor de trabajos en al menos dos, pero todos son demasiado Jolly Complicated para vivir en GHC. Tuvimos una implementación de boxy types, pero la saqué cuando implementé el nuevo typechecker. Nadie lo entendió.
Sin embargo, la gente tan a menudo escriben
runST $ do ...
que en GHC 7 He implementado una regla de escribir especial, sólo para usos infijas de ($)
. Solo piense en (f $ x)
como una nueva forma sintáctica , con la obvia regla de tipeo, y listo.
Su segundo ejemplo falla porque no existe tal regla para (.)
.
If '($)' podría recibir una firma de tipo depedently como '($): forall (a: *) (b: a -> *). ((x: a) -> b x) -> (x: a) -> b x' funcionaría sin trucos de GHC, y de manera similar para '(.)'. – danr