C'è un modo per scartare un tipo da un Io Monad?
Domanda
Ho questa funzione molto semplice
import qualified Data.ByteString.Lazy as B
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
readJFile :: IO (Maybe Response)
readJFile = parsing >>= (\d ->
case d of
Left err -> return Nothing
Right ps -> return (Just ps))
where parsing = fmap eitherDecode getJson :: IO (Either String Response)
.
Dove jsonFile
è un percorso verso un file sul mio harddrive (perdonare la mancanza di notazione, ma ho trovato questo più chiaro con)
La mia domanda è;C'è un modo per me di abbandonare la parte IO
, quindi posso lavorare con il bytestring da solo?
So che puoi modellare la partita su alcune monadi come Either
e Maybe
per ottenere i loro valori, ma puoi fare qualcosa di simile con IO
?
o espresso in modo diverso: c'è un modo per me per rendere readJFile
Return Maybe Response
senza IO?
Soluzione
Per espandere i miei commenti, ecco come puoi farlo:
getJson :: IO B.ByteString
getJson = B.readFile jsonFile -- as before
readJFile :: B.ByteString -> Maybe Response -- look, no IO
readJFile b = case eitherDecode b of
Left err -> Nothing
Right ps -> Just ps
.
Alla fine, si combina di nuovo tutto in un'azione IO:
getAndProcess :: IO (Maybe Response)
getAndProcess = do
b <- getJson
return (readJFile b)
. Altri suggerimenti
Non hai mai bisogno di "trascinare una monada" attraverso qualsiasi funzione, a meno che non siano tutte dotate di fare in realtà. Sollevare l'intera catena nella monaca con fmap
(o liftM
/ liftM2
/ ...).
Ad esempio,
f1 :: B.ByteString -> K
f2 :: K -> I
f3 :: K -> J
f4 :: I -> J -> M
.
E la tua intera cosa dovrebbe essere come
m :: M
m = let k = "f1 getJson"
in f4 (f2 k) (f3 k)
.
La tua puoi semplicemente fare
m = fmap (\b -> let k = f1 b
in f4 (f2 k) (f3 k) )
getJson
.
Per inciso, questo potrebbe sembrare più bello con la notazione do
:
m = do
b <- getJson
return $ let k = f1 b
in f4 (f2 k) (f3 k)
.
.
riguardo a modifica e la domanda
.C'è un modo per farmi guadagnare
readJFile
Maybe Response
senza ilIO
?
no , che non può funzionare, perché readJFile
ha bisogno di fare IO. Non c'è modo di sfuggire al IO
Monad allora, questo è l'intero punto di questo! (Bene, c'è unsafePerformIO
come dice Ricardo, ma questa è sicuramente una domanda valida per questo.)
Se è la stregazza del disimballaggio dei valori generacoli Maybe
nella monade IO
, e le firme con parente in esse, potresti voler guardare a Trasformatore MaybeT
.
readJFile' :: MaybeT IO Response
readJFile' = do
b <- liftIO getJson
case eitherDecode b of
Left err -> mzero
Right ps -> return ps
. In generale, sì, c'è un modo. Accompagnato da un sacco di "ma", ma c'è. Stai chiedendo cosa si chiama un'operazione non sicura IO : system.io.unsafe . È usato per scrivere involucri quando si chiama di solito alle librerie esterne, non è qualcosa da ricorrere al normale codice Haskell.
Fondamentalmente, è possibile chiamare unsafePerformIO :: IO a -> a
che fa esattamente quello che desideri, si spoglia la parte IO
e ti restituisce il valore avvolto del tipo a
. Ma se guardi la documentazione, ci sono una serie di requisiti che dovresti garantirti al sistema, che finiscono tutti nella stessa idea: anche se hai eseguito l'operazione tramite IO, la risposta dovrebbe essere il risultato di a Funzione, come previsto da qualsiasi altra funzione Haskell che non funziona in IO
: dovrebbe sempre avere lo stesso risultato senza effetti collaterali, in base ai valori di input.
Qui, dato il tuo codice, questo ovviamente non è il caso , dal momento che stai leggendo da un file. Dovresti continuare a lavorare all'interno dell'Io Monad, chiamando il tuo readJFile
da un'altra funzione con il tipo di risultato IO something
. Quindi, sarai in grado di leggere il valore all'interno del wrapper IO
(essendo in IO
da solo), lavora su di esso, quindi riavvolgere il risultato in un altro IO
quando si ritorna.
No, there is no safe way to get a value out of the IO monad. Instead you should do the work inside the IO monad by applying functions with fmap or bind (>>=). Also you should use decode instead of eitherDecode when you want your result to be in Maybe.
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode
readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON
You could also use do notation if that is clearer to you:
readJFile :: IO (Maybe Response)
readJFile = do
bytestring <- getJson
return $ decode bytestring
Note that you dont even need the parseResponse function since readJFile specifies the type.