Bonsoir,
Il y a peu, on m’a demandé d’écrire un programme qui récupère un flux Atom XML et qui ne récupère que le titre et l’url des éléments (qui en l’occurence sont des billets aggrégés sur Planet OCaml).
J’ai hésité entre OCaml, C++ et Haskell. Plutôt tenté par du fonctionnel, mon choix s’est vite porté sur Haskell grâce au nombre impressionnant de paquets présents sur Hackage.
J’ai donc opté pour le paquet feed pour la gestion d’Atom et download pour la récupération du XML distant, qui se situe ici).
Haskell Logo
Alors, voyons voir… Premièrement, on importe les modules dont on a besoin, évidemment.
import Network.Download import Text.Atom.Feed import Text.Feed.Import import Text.Feed.Types |
Ensuite, on définit l’adresse du fichier atom.xml… Toujours rien de bien sorcier.
url = "http://planet.ocamlcore.org/atom.xml" |
Puis on se lance dans main !
main = do putStrLn "*** Recent blog posts ***" Right src <- openURIString url |
Ici, on affiche simplement un message dans la console, puis on récupère le contenu du fichier dans la chaîne de caractères src.
Right est l’un des constructeurs du type Either, ne vous en préoccupez pas trop.
Ensuite, on s’occupe à proprement parler du flux Atom…
let Just (AtomFeed is) = parseFeedString src |
Just est un constructeur du type Maybe, qui permet de gérer les erreurs (Just mavaleur si pas eu d’erreur, Nothing si une erreur). AtomFeed est lui un constructeur pour le type Feed précisant que c’est un flux Atom, et non RSS1 ou RSS2 par exemple (qui sont eux aussi gérés par ce même paquet). La fonction parseFeedString prend donc une chaîne (ici src) et retourne un flux Atom… C’est là, en utilisant is, que l’on va pouvoir récupérer les différents éléments du XML au format Atom.
Bon, et si on récupérait les différentes entrées de notre flux (ici ce sont des billets de blog) ?
let entries = feedEntries is |
Ceci nous retourne donc une liste d’Entry. Et c’est parti, on va récupérer les informations qu’il nous faut, puis les afficher !
let infos = map (\e -> (entryTitle e, entryId e)) entries mapM_ (\(x,y) -> putStrLn $ (stringize x) ++ ": " ++ y) infos |
Oui, oui, ça se complique un peu.
Tout d’abord, qu’est-ce que infos ? map transforme chaque élément en l’image de l’élément donné par son premier argument, qui est donc une fonction. Ici, on va transformer chaque Entry en un couple (titre, id) associé à notre entrée, où id se trouve être l’URL originale du billet.
Bon infos est donc la liste des couples (titre, url). Ah ? Ce n’étant pas tellement cette ligne qui vous faisait peur mais la suivante ?
Bon, sans rentrer en détail dans les monades (la page Monad du HaskellWiki le faisant mieux que moi, et surtout donnant d’excellents liens pour comprendre de quoi il s’agit, où c’est utilisé, etc), on se trouve dans la monade IO. mapM_ se retrouve donc avec le type :
Notre [a], c’est infos, donc notre liste de couples…
(a -> IO b) est effectivement le type de notre fonction anonyme, que je me permets de vous montrer à part ici :
\(x,y) -> putStrLn $ (stringize x) ++ ": " ++ y |
A un couple (titre,url), elle associe un appel à putStrLn, qui retourne IO (). Donc le type b est en fait (). mapM_ ne faut qu’effectuer des actions dans une monade donnée, en ignorant le résultat de chacune au lieu de les placer des une liste comme le fait son homologue mapM
Ah oui, j’oubliais, stringize, qui est définie juste après fonction main.
stringize :: TextContent -> String stringize (TextString s) = s stringize _ = error "shoud not be called on something else than TextString" |
Elle me permet juste de passer d’une valeur construite avec TextString, donc de type TextContent, à la valeur qui est en fait englobée par ce type, de type String.
Voilà donc le code complet :
import Network.Download import Text.Atom.Feed import Text.Feed.Import import Text.Feed.Types url = "http://planet.ocamlcore.org/atom.xml" main = do putStrLn "*** Recent blog posts ***" Right src <- openURIString url let Just (AtomFeed is) = parseFeedString src let entries = feedEntries is let infos = map (\e -> (entryTitle e, entryId e)) entries mapM_ (\(x,y) -> putStrLn $ (stringize x) ++ ": " ++ y) infos stringize :: TextContent -> String stringize (TextString s) = s stringize _ = "stringize Error" |
La compilation :
$ ghc -package download -o cwn cwn.hs
Et un exemple d’execution :
$ ./cwn
*** Recent blog posts ***
ocaml-text: http://forge.ocamlcore.org/projects/ocaml-text/
0.1.3 sources now in subversion: http://forge.ocamlcore.org/forum/forum.php?forum_id=355
Sudoku in ocamljs, part 2: RPC over HTTP: tag:blogger.com,1999:blog-1445545651031573301.post-3490486535879812384
Caml Weekly News, 28 Apr 2009: http://alan.petitepomme.net/cwn/2009.04.28.html
Bouncing Ball in OCaml with OCamlSDL: http://blog.mestan.fr/?p=31
Sudoku in ocamljs, part 1: DOM programming: tag:blogger.com,1999:blog-1445545651031573301.post-4574121943207730951
Using OCaml’s module functors to provide monadic contexts for Batteries: http://blog.mestan.fr/?p=30
Lastfm no longer free as in free beer (and some bits about xml in OCaml): http://blog.rastageeks.org/spip.php?article34
Last lecture: http://dutherenverseauborddelatable.wordpress.com/?p=571
Liquidsoap now supports AAC+ encoding.: http://blog.rastageeks.org/spip.php?article33
Alors, pas si « académique » que ça le fonctionnel, non ? ![]()
Tout ça en 18 lignes, lignes vides comprises, 15 non comprises.
Enjoy.


#1 by credit loans - mars 6th, 2010 at 02:52
People in the world get the loan from different banks, just because it’s comfortable and fast.