confusione haskell con ContT, callCC, quando
-
19-09-2019 - |
Domanda
Continuando tentativo di dare un senso a ContT e amici. Si prega di prendere in considerazione il codice (assurdo ma illustrativo) di seguito:
v :: IO (Either String [String])
v = return $ Left "Error message"
doit :: IO (Either String ())
doit = (flip runContT return) $ callCC $ \k -> do
x <- liftIO $ v
x2 <- either (k . Left) return x
when True $ k (Left "Error message 2")
-- k (Left "Error message 3")
return $ Right () -- success
Questo codice non viene compilato. Tuttavia, se la sostituzione del when
con la chiamata k commentata sotto di esso, si compila. Cosa sta succedendo?
In alternativa, se io commento la linea x2, si compila anche. ???
Ovviamente, questa è una versione distillata del codice originale e quindi tutti gli elementi hanno uno scopo. Apprezzare aiuto esplicativo su quello che sta succedendo e come risolvere il problema. Grazie.
Soluzione
Il problema qui ha a che fare con i tipi di when
e either
, senza niente di particolare a ContT:
when :: forall (m :: * -> *). (Monad m) => Bool -> m () -> m ()
either :: forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
Il secondo argomento deve essere di tipo m ()
per qualche monad m
. La linea when
del codice potrebbe quindi essere modificato in questo modo:
when True $ k (Left "Error message 2") >> return ()
per rendere il codice di compilazione. Questo non è probabilmente quello che si vuole fare, ma ci dà un suggerimento di quello che potrebbe essere sbagliato:. Tipo di k
è stato dedotto di essere qualcosa di sgradevole per when
Ora, per la firma either
: notare che i due argomenti di either
devono essere funzioni che producono risultati dello stesso tipo. Il tipo di return
qui è determinato dal tipo di x
, che è a sua volta fissato dalla firma esplicito su v
. Così il bit (k . Left)
deve avere lo stesso tipo; questo a sua volta fissa il tipo di k
a (GHC-determinato)
k :: Either String () -> ContT (Either String ()) IO [String]
Ciò è incompatibile con le aspettative di when
.
Quando commentare la linea x2
, tuttavia, il suo effetto sulla vista del tipo di controllo del codice viene rimosso, in modo k
non è più costretto in un tipo scomodo ed è libero di assumere il tipo
k :: Either [Char] () -> ContT (Either [Char] ()) IO ()
che va bene nel libro di when
. Così, il codice viene compilato.
Come nota finale, ho usato i punti di interruzione di impianto GHCi per ottenere l'esatto tipo di k
sotto le due scenari - Sono neanche lontanamente esperto abbastanza per scriverle a mano e di essere in alcun modo garantita la loro correttezza. :-) Utilizzare :break ModuleName line-number column-number
di provarlo.