Es allí una manera de desenvolver un tipo de una mónada IO?
Pregunta
Tengo esta función muy sencilla
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)
donde jsonFile
es una ruta a un archivo en mi disco duro (perdón por la falta de notación, pero he encontrado esto más claro a trabajar)
mi pregunta es;hay una manera para mí para deshacerse de la IO
parte para que yo pueda trabajar con el bytestring solo?
Yo sé que usted puede patrón coincide en ciertos mónadas como Either
y Maybe
para obtener sus valores, pero se puede hacer algo similar con IO
?
O expresado de manera diferente:hay una manera para mí para hacer readJFile
volver Maybe Response
sin el IO?
Solución
Para ampliar mis comentarios, aquí es cómo puedes hacerlo:
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
Al final, combinas todo en una acción de IO nuevamente:
getAndProcess :: IO (Maybe Response)
getAndProcess = do
b <- getJson
return (readJFile b)
Otros consejos
Usted nunca necesita "arrastrar una mónada" a través de cualquiera de las funciones, a menos que todos ellos necesitan para hacer realidad IO.Simplemente levante el conjunto de la cadena en la mónada con fmap
(o liftM
/ liftM2
/ ...).
Por ejemplo,
f1 :: B.ByteString -> K
f2 :: K -> I
f3 :: K -> J
f4 :: I -> J -> M
y toda su cosa se supone que para ser como
m :: M
m = let k = "f1 getJson"
in f4 (f2 k) (f3 k)
Usted puede simplemente hacer
m = fmap (\b -> let k = f1 b
in f4 (f2 k) (f3 k) )
getJson
Por cierto, esto podría ser más agradable con do
notación:
m = do
b <- getJson
return $ let k = f1 b
in f4 (f2 k) (f3 k)
Sobre la edición y la pregunta
hay una manera para mí para hacer
readJFile
volverMaybe Response
sin elIO
?
No, que no puede trabajar, porque readJFile
no es necesario hacer IO.No hay manera de escapar de la IO
mónada entonces, que es el punto entero de esto!(Bueno, hay unsafePerformIO
como dice Ricardo, pero este definitivamente no es una aplicación válida para ello.)
Si es el clunkiness de desembalaje Maybe
los valores en la IO
mónada, y las firmas con paréntesis en ellos, usted puede querer que se ve en la MaybeT
transformador.
readJFile' :: MaybeT IO Response
readJFile' = do
b <- liftIO getJson
case eitherDecode b of
Left err -> mzero
Right ps -> return ps
En general, sí, hay una manera.Acompañado por una gran cantidad de "pero", pero no es.Estás pidiendo lo que se llama un inseguro operación de e / s: Sistema.IO.Inseguro.Se utiliza para escribir contenedores cuando se llama a bibliotecas externas generalmente, no es algo que recurrir a regular Haskell código.
Básicamente, usted puede llamar a unsafePerformIO :: IO a -> a
que hace exactamente lo que usted desea, elimina la IO
parte y le da la espalda envuelto valor de tipo a
.Pero, si se mira en la documentación, hay una serie de requisitos que debe garantizarse a sí mismo en el sistema, que todos terminan en la misma idea:incluso a pesar de que se realizó la operación a través de IO, la respuesta debería ser el resultado de una función, como se espera de cualquier otra función haskell que no opera en IO
:debe tener siempre el mismo resultado sin efectos secundarios, que sólo se basa en los valores de entrada.
Aquí, dado su código, obviamente, esto NO es el caso, ya que usted está leyendo desde un archivo.Usted debe seguir trabajando dentro de la mónada IO, llamando a su readJFile
desde dentro de otra función con el tipo de resultado IO something
.A continuación, podrás leer el valor en el IO
contenedor (está en IO
usted mismo), trabajar en él y, a continuación, vuelva a envolver el resultado en otra IO
cuando regresan.
No, No hay ninguna forma segura de obtener un valor de la mónada IO.En lugar usted debe hacer el trabajo en el interior de la mónada IO mediante la aplicación de funciones con fmap o bind (>>=).También debe utilizar decodificar en lugar de eitherDecode cuando usted quiere que su resultado sea tal vez.
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode
readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON
Usted podría también hacer uso de la notación si que es claro para usted:
readJFile :: IO (Maybe Response)
readJFile = do
bytestring <- getJson
return $ decode bytestring
Tenga en cuenta que usted no necesita de la parseResponse función desde readJFile especifica el tipo.