2015-06-22 11 views
6

Sto utilizzando una libreria grafica in Haskell chiamata Threepenny-GUI. In questa libreria la funzione principale restituisce un oggetto monad UI. Questo mi causa molto mal di testa come quando tento di decomprimere valori IO in variabili locali. Ricevo errori lamentando diversi tipi di monade.Haskell do clausola con più tipi di monade

Ecco un esempio del mio problema. Questa è una versione leggermente modificata della funzione principale di serie, come dato da esempio di codice di tre soldi-GUI:

main :: IO() 
main = startGUI defaultConfig setup 

setup :: Window -> UI() 
setup w = do 

labelsAndValues <- shuffle [1..10] 

shuffle :: [Int] -> IO [Int] 
shuffle [] = return [] 
shuffle xs = do randomPosition <- getStdRandom (randomR (0, length xs - 1)) 
       let (left, (a:right)) = splitAt randomPosition xs 
       fmap (a:) (shuffle (left ++ right)) 

Si prega di notare la quinta riga:

labelsAndValues <- shuffle [1..10] 

che restituisce il seguente errore:

Couldn't match type ‘IO’ with ‘UI’ 
Expected type: UI [Int] 
    Actual type: IO [Int] 
In a stmt of a 'do' block: labelsAndValues <- shuffle [1 .. 10] 

Per quanto riguarda la mia domanda, come posso decomprimere la funzione IO utilizzando la notazione a freccia standard (<-) e continuare a Sono variabili come IO() anziché UI(), quindi posso facilmente passarle ad altre funzioni.

Attualmente, l'unica soluzione che ho trovato è stato quello di utilizzare liftIO, ma questo fa sì che la conversione al tipo UI Monade, mentre io in realtà voglio continuare a utilizzare il tipo di IO.

+1

Non è possibile modificare la modalità Monad a metà del blocco di do – Squidly

+1

Cosa si intende per continuare a utilizzare il tipo di I/O? Sarebbe 'liftIO $ fare ... un blocco di codice IO ...' essere quello che stai cercando? –

+0

In seguito alle conclusioni di questo thread, ho aperto una nuova domanda su come integrare la stampa. qualsiasi aiuto sarebbe molto apprezzato - http://stackoverflow.com/questions/30988595/haskell-ui-do-clause-how-to-print – vondip

risposta

7

Un blocco do è per un tipo specifico di monade, non è possibile semplicemente modificare il tipo nel mezzo.

È possibile trasformare l'azione oppure nidificarla all'interno di do. Molte volte le trasformazioni saranno pronte per te. È possibile, ad esempio, avere un nidificato do che funziona con io e quindi convertirlo solo nel punto di interazione.

Nel vostro caso, una funzione liftIOLater viene offerta per gestire questo per voi dal pacchetto ThreePennyUI.

liftIOLater :: IO() -> UI()

Schedule an IO action to be run later.

Al fine di eseguire la conversione inversa, è possibile utilizzare runUI:

runUI :: Window -> UI a -> IO a

Execute an UI action in a particular browser window. Also runs all scheduled IO action.

+0

Nel mio caso sto provando a utilizzare un elenco int casuale e quindi visualizzarlo per schermare, non è chiaro come separare l'azione io da eseguire in un secondo momento, quindi – vondip

+0

'UI'è un'istanza di' MonadIO', quindi 'liftIO' è sufficiente per gestire il problema dell'OP (cf. [risposta alla domanda di follow-up] (http://stackoverflow.com/a/30988739/2751851)). 'liftIOLater' è pensato solo per essere utilizzato in una manciata di casi più complicati (ad esempio, azioni che devono essere eseguite durante l'inizializzazione della GUI solo dopo che il resto della GUI è stato impostato). – duplode

2

Sotto la copertura, fare è semplicemente di zucchero sintattico per >> = (bind) e lasciate:

do { x<-e; es } = e >>= \x -> do { es } 
do { e; es } = e >> do { es } 
do { e }  = e 
do {let ds; es} = let ds in do {es} 

E il tipo di bind:

(>>=) :: Monad m => a -> (a -> m b) -> m b 

Quindi sì "supporta" solo un Monad

4

Questo è più un commento esteso - non affronta la domanda principale, ma l'implementazione di shufffle. Ci sono 2 problemi con esso:

  1. L'implementazione è inefficiente - O (n^2).
  2. IO non è il tipo giusto per questo - lo shuffle non ha effetti collaterali generali, ha solo bisogno di una fonte di casualità.

per (1) ci sono diverse soluzioni: Uno è quello di utilizzare Seq e la sua index, che è O (log n), che renderebbe shuffleO (n log n). Oppure è possibile utilizzare STarrays e uno degli standard algorithms per ottenere O (n).

Per (2), tutto ciò che serve è l'inserimento di un generatore casuale, non la piena potenza di IO. C'è già una bella libreria MonadRandom che definisce una monade (e una classe tipo) per calcoli randomizzati. E un altro pacchetto fornisce già lo shuffle function. Poiché IO è un'istanza di MonadRandom, è possibile utilizzare semplicemente shuffle direttamente in sostituzione della propria funzione.