Existe uma maneira de desvendar um tipo de uma e / s de mônada?
Pergunta
Eu tenho isso muito simples função
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)
onde jsonFile
é um caminho para um arquivo no disco rígido (perdoem a falta do que fazer de notação, mas eu achei isso mais claro para trabalhar com)
a minha pergunta é;há para mim uma forma de abandonar o IO
peça para que eu possa trabalhar com o bytestring sozinho?
Eu sei que você pode correspondência de padrão em determinados campos como Either
e Maybe
para obter os seus valores, mas você pode fazer algo semelhante com IO
?
Ou expressaram de forma diferente:existe uma maneira de me fazer readJFile
retorno Maybe Response
sem o IO?
Solução
Para expandir os meus comentários, aqui está como você pode fazer isso:
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
No final, depois de combinar tudo em um IO ação novamente:
getAndProcess :: IO (Maybe Response)
getAndProcess = do
b <- getJson
return (readJFile b)
Outras dicas
Você nunca precisa para "arrastar uma mônada" através de quaisquer funções, a menos que todos eles precisam realmente fazer IO.Apenas levante a toda a cadeia para a mônada com fmap
(ou liftM
/ liftM2
/ ...).
Por exemplo,
f1 :: B.ByteString -> K
f2 :: K -> I
f3 :: K -> J
f4 :: I -> J -> M
e toda a sua coisa é suposto para ser como
m :: M
m = let k = "f1 getJson"
in f4 (f2 k) (f3 k)
O que você pode simplesmente fazer
m = fmap (\b -> let k = f1 b
in f4 (f2 k) (f3 k) )
getJson
Aliás, isso pode parecer mais agradável com do
notação:
m = do
b <- getJson
return $ let k = f1 b
in f4 (f2 k) (f3 k)
Sobre você editar e a questão
existe uma maneira de me fazer
readJFile
retornoMaybe Response
sem oIO
?
Nenhum, que não pode trabalhar, porque readJFile
não precisa fazer IO.Não há nenhuma maneira de escapar dos IO
mônada em seguida, que é o ponto de todo ele!(Bem, não há unsafePerformIO
como o Ricardo diz, mas este definitivamente não é um aplicativo válido para ele.)
Se é o clunkiness de descompactar Maybe
valores em IO
mônada, e as assinaturas com parens neles, você pode querer olha para o MaybeT
transformador.
readJFile' :: MaybeT IO Response
readJFile' = do
b <- liftIO getJson
case eitherDecode b of
Left err -> mzero
Right ps -> return ps
Em geral, sim, há um caminho.Acompanhado por um monte de "mas", mas não é.Você está pedindo para o que é chamado de inseguro operação de e / s: Do sistema.IO.Inseguro.Ele é usado para escrever mensagens publicitárias ao chamar a bibliotecas externas, geralmente, não é algo que recorrer a regular Haskell código.
Basicamente, você pode chamar unsafePerformIO :: IO a -> a
o que faz exatamente o que você quer, ele retira o IO
parte e dá-lhe de volta envolto valor do tipo a
.Mas, se você olhar para a documentação, há uma série de requisitos que você deve garantir-se ao sistema, o que na mesma idéia:mesmo que você executou a operação através de IO, a resposta deve ser o resultado de uma função, como esperado de qualquer outro haskell função que não operam no IO
:ele deve ter sempre o mesmo resultado, sem efeitos colaterais, apenas com base nos valores de entrada.
Aqui, dado o seu código, isso obviamente NÃO é o caso, já que você está lendo de um arquivo.Você deve apenas continuar a trabalhar no IO mônada, chamando seu readJFile
de dentro de outra função com o tipo de resultado IO something
.Em seguida, você vai ser capaz de ler o valor no IO
wrapper (sendo em IO
si mesmo), trabalhar nele e, em seguida, re-moldar o resultado em outro IO
quando voltar.
Não, não há nenhuma forma segura de obter um valor de IO mônada.Em vez disso, você deve fazer o trabalho dentro do IO mônada pela aplicação de funções com fmap ou ligar (>>=).Além disso, você deve usar decodificar em vez de eitherDecode quando você deseja que o seu resultado seja Talvez.
getJson :: IO B.ByteString
getJson = B.readFile jsonFile
parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode
readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON
Você também pode usar o fazer notação se que é mais clara para você:
readJFile :: IO (Maybe Response)
readJFile = do
bytestring <- getJson
return $ decode bytestring
Note que você não precisa mesmo de o parseResponse função desde readJFile especifica o tipo.