L'inferenza di tipo in presenza di tipi di rango superiore è indecidibile. L'algoritmo di inferenza del tipo di GHC semplifica alcune ipotesi, che lo rendono incompleto. Questi includono:
GHC assumerà la variabile legata da una lambda è un monotipo (? Non contiene (leader) forall
s)
Quando si utilizza una funzione polimorfica, GHC assumerà che le variabili di tipo gratuiti a suo tipo saranno istanziati in monotipi
Entrambe queste ipotesi possono essere sovrascritti se chiedete GHC cortesemente di utilizzare un particolare polytype invece.
Ora, come prevedete che il vostro programma scriva check?
- Affinché
run fs
digitare controllare, faremmo meglio avere fs :: forall s. [ST.STRef s Int -> ST.ST s Int]
- Così, secondo il primo punto di cui sopra deve scrivere questa firma tipo sul lambda che lo lega:
\(fs :: forall s. [ST.STRef s Int -> ST.ST s Int]) -> ...
(usando ScopedTypeVariables
)
Considerare ora l'uso di >>=
. Il suo tipo è Monad m => m a -> (a -> m b) -> m b
. Ma, abbiamo bisogno di a = forall s. [ST.STRef s Int -> ST.ST s Int]
. Quindi, in base al secondo punto di cui sopra abbiamo bisogno di dare a questo >>=
una firma tipo, come in
... `op` (\(fs :: forall s. [ST.STRef s Int -> ST.ST s Int]) -> ...)
where op :: Monad m
=> m (forall s. [ST.STRef s Int -> ST.ST s Int])
-> ((forall s. [ST.STRef s Int -> ST.ST s Int]) -> m b)
-> m b
op = (>>=)
- Ora ci imbattiamo in un nuovo tipo di problema. In questa applicazione di
op
, il primo argomento ha tipo Either String (forall s. [ST.STRef s Int -> ST.ST s Int])
. L'applicazione di un costruttore di tipi (diverso da (->)
) a un politipo non è consentita, a meno che non si attivino i tipi di errore (danneggiati). Tuttavia, possiamo accenderlo e continuare ...
- Immergersi nel primo argomento, possiamo vedere che avremo bisogno di
return :: Monad m => a -> m a
istanziato a a = forall s. ST.STRef s Int -> ST.ST s Int
. Quindi, abbiamo bisogno di aggiungere un'altra firma tipo di return
- Allo stesso modo avremo bisogno
mapM :: Monad m => (a -> m b) -> [a] -> m [b]
un'istanza in b = forall s. ST.STRef s Int -> ST.ST s Int
Se si sta prestando molta attenzione si nota ancora un altro problema: il risultato della mapM
ha digitare
Either String [forall s. ST.STRef s Int -> ST.ST s Int]
ma l'argomento di (>>=)
deve essere di tipo
Either String (forall s. [ST.STRef s Int -> ST.ST s Int])
e si avrà bisogno di convertire tra questi.In realtà si tratta di un no-op, ma GHC non è abbastanza intelligente da sapere che, in modo da avere per fare una conversione lineare-tempo, qualcosa di simile a
liftM (\x -> map (\(y :: forall s. ST.STRef s Int -> ST.ST s Int) -> y) x)
(tranne liftM
avrà bisogno di ancora un altro firma tipo)
Morale della storia: puoi farlo, ma non dovresti.
È in genere hanno un tempo più facile se si nasconde il tuo forall
s newtypes dentro come
newtype S s = S { unS :: forall s. ST.STRef s Int -> ST.ST s Int }
che rende i punti in cui il polimorfismo viene introdotto più esplicito nel programma (tramite le occorrenze di S
e unS
).
fonte
2015-08-03 22:48:44
[** 'sequenza' **] (https://www.haskell.org/hoogle/?hoogle=sequence)? –
@ recursion.ninja è un ottimo suggerimento ma sfortunatamente non sembra fare la differenza. Ti aspetteresti che ci siano istanze di 'sequenza' con risultati diversi da' mapM'? – ryachza
Sto formulando una risposta ora –