Devo forzare la valutazione del valore puro nella monade IO
. Sto scrivendo l'interfaccia di livello superiore ai collegamenti C. Al livello inferiore ho, ad esempio, la funzione newFile
e la funzione freeFile
. newFile
restituisce qualche id, oggetto opaco che ho definito al livello inferiore. Non puoi fare praticamente nulla con questo, ma lo per usarlo per liberare il file e calcola semplicemente qualcosa associato a quel file.Come forzare correttamente la valutazione del valore puro in IO monade?
Così, ho (semplificato):
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path -- ‘fid’ stands for “file id”
let x = runGetter g fid
freeFile fid
return x
Questa è la versione iniziale della funzione. Dobbiamo calcolare x
prima che venga chiamato freeFile
. (Il codice funziona, se tolgo freeFile
è tutto bene, ma voglio liberare la risorsa, si sa.)
Primo tentativo (useremo seq
di valutazione “forza”):
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
x `seq` freeFile fid
return x
Errore di segmentazione. Proseguire dritto alla documentazione di seq
:
Il valore di
seq a b
è basso sea
è basso, e comunque pari ab
.seq
viene generalmente introdotto per migliorare le prestazioni evitando la pigrizia non necessaria .Una nota su ordine di valutazione: l'espressione
seq a b
non garantisce chea
verrà valutato primab
. L'unica garanzia fornita daseq
è che entrambia
eb
verranno valutati prima cheseq
restituisca un valore . In particolare, ciò significa cheb
può essere valutato prima delloa
. Se è necessario garantire un ordine specifico di valutazione, è necessario utilizzare la funzionepseq
dal pacchetto "parallelo".
Una nota positiva, infatti, ho visto persone che rivendicano cose diverse sull'ordine di valutazione in questo caso. Che mi dici di pseq
? Devo dipendere da parallel
solo per il pseq
, hmm ... potrebbe esserci un altro modo.
{-# LANGUAGE BangPatterns #-}
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let !x = runGetter g fid
freeFile fid
return x
Errore di segmentazione. Bene, that answer non funziona nel mio caso . Ma suggerisce evaluate
, proviamo troppo:
Control.Exception (evaluate)
Control.Monad (void)
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
void $ evaluate x
freeFile fid
return x
Segmentation fault. Forse dovremmo usare il valore restituito da evaluate
?
Control.Exception (evaluate)
Control.Monad (void)
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
x' <- evaluate x
freeFile fid
return x'
No, cattiva idea.Forse potremmo catena seq
:
execGetter :: FilePath -> TagGetter a -> IO a
execGetter path g = do
fid <- newFile path
let x = runGetter g fid
x `seq` freeFile fid `seq` return x
Questo funziona. Ma è questo il modo giusto per farlo? Forse funziona solo a causa della logica di ottimizzazione volatile ? Non lo so. Se seq
si associa in questo caso a , in base a tale descrizione, entrambi x
e freeFile
vengono valutati quando return x
restituisce il suo valore. Ma ancora, quale di questi, x
o freeFile
viene valutato per primo? Dal momento che non ottengo errori di seg, è necessario essere x
, ma questo risultato è affidabile? Sai come forzare la valutazione di x
prima del freeFile
correttamente?
'seq' non introdurrà segfaults a meno che non ci sono stati già * * segfaults lì. Sei sicuro che il problema sia * effettivamente * un segfault, o stai usando quel termine per indicare qualcosa di diverso dal solito significato? È possibile che sia 'freeFile' che sta segmentando? (Prendo atto che i due che hai dichiarato di non eseguire anche segfault non eseguono 'freeFile' - nonostante il modo in cui può sembrare a un occhio inesperto!) Inoltre, a meno che tu non stia usando il male lazy IO da qualche parte (es. readFile' o 'unsafeInterleaveIO'), non è necessario forzare' x'. –
Inoltre, sospetto che potresti avere frainteso la nota su 'seq'. Prendi nota con attenzione della distinzione tra * valutare * un'azione 'IO' - cioè capire quale IO fare, in particolare - e * eseguire * l'azione - cioè eseguire l'IO effettivo. Il commento parla solo di valutazione; potresti volerlo rileggere attentamente con questa distinzione in mente! –
Credo che 'x \' seq \ 'freeFile fid \' seq \ 'restituisce x' non libera il file,' seq'ing una azione IO non lo esegue - questa espressione dice "Valuta' x' e 'freeFile fid 'a WHNF prima di restituire' return x' "- ma, la valutazione di un'azione IO non la" esegue ". – user2407038