Question

J'ai cette fonction très simple

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)

jsonFile est un chemin vers un fichier sur mon disque dur (pardonnez le manque de notation do, mais j'ai trouvé cela plus clair à utiliser)

Ma question est;y a-t-il un moyen pour moi d'abandonner le IO partie pour que je puisse travailler avec la chaîne d'octets seule ?

Je sais que vous pouvez faire correspondre des modèles sur certaines monades comme Either et Maybe pour faire ressortir leurs valeurs, mais pouvez-vous faire quelque chose de similaire avec IO?

Ou exprimé différemment :y a-t-il un moyen pour moi de faire readJFile retour Maybe Response sans l'IO ?

Était-ce utile?

La solution

Pour développer mes commentaires, voici comment procéder :

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

En fin de compte, vous combinez à nouveau tout en une seule action IO :

getAndProcess :: IO (Maybe Response)
getAndProcess = do
  b <- getJson
  return (readJFile b)

Autres conseils

Vous n'avez jamais besoin de "faire glisser une monade" à travers des fonctions, à moins qu'elles n'aient toutes besoin de réellement effectuer des E/S.Soulevez simplement toute la chaîne dans la monade avec fmap (ou liftM / liftM2 / ...).

Par exemple,

f1 :: B.ByteString -> K
f2 :: K -> I
f3 :: K -> J
f4 :: I -> J -> M

et tout ton truc est censé être comme

m :: M
m = let k = "f1 getJson"
    in f4 (f2 k) (f3 k)

Vous pouvez simplement le faire

m = fmap (\b -> let k = f1 b
                in f4 (f2 k) (f3 k) )
    getJson

À propos, cela pourrait paraître plus joli avec do notation:

m = do
  b <- getJson
  return $ let k = f1 b
           in f4 (f2 k) (f3 k)

Concernant votre modification et la question

y a-t-il un moyen pour moi de faire readJFile retour Maybe Response sans le IO?

Non, ça ne peut pas marcher, parce que readJFile doit faire IO.Il n'y a aucun moyen d'échapper au IO monade alors, c'est tout l'intérêt !(Eh bien, il y a unsafePerformIO comme le dit Ricardo, mais ce n'est certainement pas une application valable.)

Si c'est la maladresse du déballage Maybe valeurs dans le IO monade, et les signatures avec des parenthèses, vous voudrez peut-être regarder la MaybeT transformateur.

readJFile' :: MaybeT IO Response
readJFile' = do
   b <- liftIO getJson
   case eitherDecode b of
     Left err -> mzero
     Right ps -> return ps

En général, oui, il existe un moyen.Accompagné de beaucoup de « mais », mais il y en a.Vous demandez ce qu'on appelle un opération d'E/S dangereuse: System.IO.Unsafe.Il est généralement utilisé pour écrire des wrappers lors de l'appel à des bibliothèques externes, ce n'est pas quelque chose auquel recourir dans le code Haskell standard.

En gros, vous pouvez appeler unsafePerformIO :: IO a -> a qui fait exactement ce que vous voulez, il supprime le IO part et vous redonne la valeur enveloppée de type a.Mais si vous regardez la documentation, vous devez vous assurer d'un certain nombre d'exigences envers le système, qui aboutissent toutes à la même idée :même si vous avez effectué l'opération via IO, la réponse doit être le résultat d'une fonction, comme prévu de toute autre fonction Haskell qui ne fonctionne pas dans IO:il devrait toujours avoir le même résultat sans effets secondaires, uniquement basé sur les valeurs d'entrée.

Ici, étant donné votre code, ce n'est évidemment PAS le cas, puisque vous lisez un fichier.Vous devez simplement continuer à travailler au sein de la monade IO, en appelant votre readJFile depuis une autre fonction avec le type de résultat IO something.Ensuite, vous pourrez lire la valeur dans le IO emballage (étant dans IO vous-même), travaillez dessus, puis réemballer le résultat dans un autre IO au retour.

Non, il n'existe aucun moyen sûr d'obtenir une valeur de la monade IO.Au lieu de cela, vous devez effectuer le travail à l'intérieur de la monade IO en appliquant des fonctions avec fmap ou bind (>>=).Vous devez également utiliser decode au lieu de l'un ou l'autreDecode lorsque vous souhaitez que votre résultat soit dans Maybe.

getJson :: IO B.ByteString
getJson = B.readFile jsonFile

parseResponse :: B.ByteString -> Maybe Response
parseResponse = decode

readJFile :: IO (Maybe Response)
readJFile = fmap parseResponse getJSON

Vous pouvez également utiliser la notation do si cela est plus clair pour vous :

readJFile :: IO (Maybe Response)
readJFile = do
    bytestring <- getJson
    return $ decode bytestring

Notez que vous n'avez même pas besoin de la fonction parseResponse puisque readJFile spécifie le type.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top