рд▓реЗрдХрд┐рди рдореИрдВрдиреЗ рдлрд┐рд░ рднреА рдХреБрдЫ рдХрд╛рдо рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ рддрд╛рдХрд┐ рдпрд╣ рджрд┐рдЦрд╛рдпрд╛ рдЬрд╛ рд╕рдХреЗ рдХрд┐ рд╣рд╛рд╕реНрдХреЗрд▓ рдореЗрдВ рдПрдХ рд╣реА рдХрд╛рдо рдХреИрд╕реЗ рдХрд┐рдпрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ , рдЕрдЧрд░ рдХреЗрд╡рд▓ ...
рдХреЙрдорд┐рдХ рдмреБрдХ рдХреА рдЬрд╛рдирдХрд╛рд░реА рдкреНрд░рд╛рдкреНрдд рдХрд░реЗрдВ
рдЪреВрдВрдХрд┐ рд╣рдореЗрдВ JSON рдХреЗ рд░реВрдк рдореЗрдВ рдЬрд╛рдирдХрд╛рд░реА рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░рдирд╛ рд╣реИ, рд╣рдо рддреБрд░рдВрдд рдПрдХ рдЙрдкрдпреЛрдЧреА рдлрд╝рдВрдХреНрд╢рди рд▓рд┐рдЦреЗрдВрдЧреЗ:
rethrow :: ( Show e ) => Exceptional ea -> IO a<br>
rethrow = switch ( throwIO . userError . show ) return<br>
<br>
jsonAt url = simpleHTTP ( getRequest url ) >>= getResponseBody >>= rethrow . decode_ json<br>
<br>
str s = member_ ( literal s ) JSON . string<br>
num n = member_ ( literal n ) JSON . number<br>
rethrow
рдлрд╝рдВрдХреНрд╢рди рдПрдХ рдЕрдкрд╡рд╛рдж
rethrow
рдлреЗрдВрдХ
rethrow
рдЬрдм рдЧрд▓рдд рддрд░реАрдХреЗ рд╕реЗ рдкрд╛рд░реНрд╕рд┐рдВрдЧ, рдФрд░
str
рдФрд░
num
JSON рдореЗрдВ рдЖрд╡рд╢реНрдпрдХ рдлрд╝реАрд▓реНрдб рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдирд╛ рдЖрд╕рд╛рди рдмрдирд╛ рджреЗрдЧрд╛ред
рдлрд┐рд░ рдЕрдВрддрд┐рдо рдХреЙрдорд┐рдХ рдФрд░ рдирдВрдмрд░ рд╕реЗ рдХреЙрдорд┐рдХ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдХреЛрдб рдЗрд╕ рддрд░рд╣ рджрд┐рдЦрд╛рдИ рджреЗрдЧрд╛:
comics = jsonAt "http://xkcd.com/info.0.json" >>= fmap ( fromIntegral . numerator ) . rethrow . decode_ ( num "num" ) <br>
<br>
comic n = jsonAt ( concat [ "http://xkcd.com/" , show n , "/info.0.json" ] ) >>= rethrow . decode_ ( str "img" <&> str "title" ) <br>
decode_
рдлрд╝рдВрдХреНрд╢рди рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдкреИрдЯрд░реНрди рдХреЗ рдЕрдиреБрд╕рд╛рд░ рдкреНрд░рд╛рдкреНрдд рдбреЗрдЯрд╛ рдХреЛ
decode_
ред
num "num"
рдХреЗ рдорд╛рдорд▓реЗ рдореЗрдВ
num "num"
рд╣рдореЗрдВ JSON рд╕реЗ рдПрдХ рд╕рдВрдЦреНрдпрд╛рддреНрдордХ рд╕рджрд╕реНрдп рдХрд╛ рдирд╛рдо рдорд┐рд▓рд╛ рд╣реИ, рдЬреЛ
str "img" <&> str "title"
str "img" <&> str "title"
- рдХреНрд░рдорд╢рдГ рдЪрд┐рддреНрд░ рдФрд░ рдирд╛рдо рдХреЗ рд▓рд┐рдП рджреЛ рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХрд╛ рдмреЗрд╡рдХреВрдлред
URL рджреНрд╡рд╛рд░рд╛ рдЪрд┐рддреНрд░ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХрд╛ рдХреЛрдб:
image url = simpleHTTP ( getRequest url ) >>= getResponseBody<br>
рдбрд╛рдЙрдирд▓реЛрдб рд╕реНрдЯреНрд░реАрдо
рд╣рдо рдПрдХ рдЕрд▓рдЧ рдХрд╛рд░реНрдп рдореЗрдВ рдПрдХ рдХреЙрдорд┐рдХ рдХрд╛ рдбрд╛рдЙрдирд▓реЛрдб рд▓рд┐рдЦрддреЗ рд╣реИрдВред
retrieve ch li = tryGet `onException` onFail where <br>
onFail = do <br>
writeChan ch ( i , Nothing ) <br>
writeLogger l Error $ "Comic " ++ show i ++ " failed to download" <br>
tryGet = do <br>
( imgUrl , title ) <- comic i<br>
imgData <- image imgUrl<br>
jpg <- writeBinaryFile fname imgData >> readJpegFile fname >>= either ( throwIO . userError . show ) return<br>
writeChan ch ( i , Just ( jpg , title ) ) <br>
writeLogger l Info $ "Comic " ++ show i ++ " downloaded" <br>
fname = show i ++ ".jpg" <br>
рдпрд╣рд╛рдВ рд╣рдордиреЗ
ch
рдЪреИрдирд▓ (
Control.Concurrent.Chan
) рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛, рдЬрд┐рд╕рд╕реЗ рд╣рдо рдбрд╛рдЙрдирд▓реЛрдб рдкрд░рд┐рдгрд╛рдо рднреЗрдЬреЗрдВрдЧреЗ, рд╕рд╛рде рд╣реА рдереНрд░реЗрдб-рд╕реЗрдл рд▓реЙрдЧ
l
ред
рдПрдЪрдкреАрдбреАрдПрдл рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреА рд╡рдХреНрд░ рдХреЗ рдХрд╛рд░рдг, рдЖрдкрдХреЛ рдкрд╣рд▓реЗ рдЫрд╡рд┐ рдХреЛ рдПрдХ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╕рд╣реЗрдЬрдирд╛ рд╣реЛрдЧрд╛, рдФрд░ рдлрд┐рд░ рдЗрд╕реЗ рдлрд┐рд░ рд╕реЗ рд╡рд╣рд╛рдВ рд╕реЗ рд▓реЛрдб рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдпрд╣ рдореЗрд░реЗ рд▓рд┐рдП рдкреВрд░реА рддрд░рд╣ рд╕реЗ рд╕реНрдкрд╖реНрдЯ рдирд╣реАрдВ рд╣реИ рдХрд┐ рд▓реЗрдЦрдХ рдиреЗ рдЬреЗрдкреАрдИрдЬреА рдХреЛ рд╕реНрд╡рдпрдВ рдЦрд░реЛрдВрдЪ рд╕реЗ (рдФрд░ рдпрд╣рд╛рдВ рддрдХ тАЛтАЛрдХрд┐ рдХреЗрд╡рд▓ рдПрдХ рдлрд╝рд╛рдЗрд▓ рд╕реЗ) рдкрд╛рд░реНрд╕ рдХреНрдпреЛрдВ рд▓рд┐рдЦрд╛, рд▓реЗрдХрд┐рди рддреИрдпрд╛рд░ рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХрд╛ рдЙрдкрдпреЛрдЧ рдирд╣реАрдВ рдХрд┐рдпрд╛
рдкреАрдбреАрдПрдл рдЬрдирд░реЗрд╢рди
рдЕрдм рдпрд╣ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рд▓рд┐рдЦрдиреЗ рдХреЗ рд▓рд╛рдпрдХ рд╣реИ рдЬреЛ рдЪрд┐рддреНрд░реЛрдВ рдХреА рд╕реВрдЪреА рд╕реЗ рдкрд░рд┐рдгрд╛рдореА рдкреАрдбреАрдПрдл рдЙрддреНрдкрдиреНрди рдХрд░реЗрдЧрд╛ред
pdf imgs = runPdf "Xkcd.pdf" doc ( PDFRect 0 0 800 600 ) $ forM_ imgs genPage where <br>
genPage ( jpeg , title ) = do <br>
img <- createPDFJpeg jpeg<br>
page <- addPage Nothing <br>
drawWithPage page ( drawText ( text ( PDFFont Times_Roman 12 ) 0 0 ( toPDFString title ) ) >> drawXObject img ) <br>
doc = PDFDocumentInfo { <br>
author = toPDFString "xkcd" , <br>
subject = toPDFString "xkcd" , <br>
pageMode = UseNone , <br>
pageLayout = OneColumn , <br>
viewerPreferences = standardViewerPrefs , <br>
compressed = False } <br>
рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, рдпрд╣ рдлрд╝рдВрдХреНрд╢рди рдХреБрдЫ рднреА рджрд┐рд▓рдЪрд╕реНрдк рдирд╣реАрдВ рд╣реИред рд╣рдо рдкреБрд╕реНрддрдХрд╛рд▓рдпреЛрдВ рд╕реЗ рд╕рдВрдмрдВрдзрд┐рдд рдХрд╛рд░реНрдпреЛрдВ рдХреЛ рдХрд╣рддреЗ рд╣реИрдВред рдХреЗрд╡рд▓ рдЕрддрд┐ рд╕реВрдХреНрд╖реНрдо рдЕрдВрддрд░ рдорд╣рддреНрд╡рдкреВрд░реНрдг рд╣реИ рдХрд┐ рдЪрд┐рддреНрд░реЛрдВ рдХреА рд╕реВрдЪреА рдЖрд▓рд╕реА рд╣реИ, рдЗрд╕рд▓рд┐рдП рдкрд╣рд▓реА рддрд╕реНрд╡реАрд░ рджрд┐рдЦрд╛рдИ рджреЗрддреЗ рд╣реА рдлрд╝рдВрдХреНрд╢рди рдХрд╛рдо рдХрд░рдирд╛ рд╢реБрд░реВ рдХрд░ рджреЗрддрд╛ рд╣реИред
рдЗрд╕реЗ рдПрдХ рд╕рд╛рде рд░рдЦрдирд╛
рдореБрдЦреНрдп рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ, рд╣рдо рд▓реЙрдЧ рдХреЛ рдЗрдирд┐рд╢рд┐рдпрд▓рд╛рдЗрдЬрд╝ рдХрд░рддреЗ рд╣реИрдВ, рдПрдХ рдЪреИрдирд▓ рдмрдирд╛рддреЗ рд╣реИрдВ рдЬрд┐рд╕рдореЗрдВ рд╣рд▓реНрдХреЗ рдзрд╛рдЧреЗ рдкрд░рд┐рдгрд╛рдо рд▓рд┐рдЦреЗрдВрдЧреЗ, рдФрд░ рдЗрд╕ рдЪреИрдирд▓ рд╕реЗ рддрд╕реНрд╡реАрд░реЛрдВ рдХреА рдПрдХ рдЖрд▓рд╕реА рд╕реВрдЪреА рдХреЗ рд╕рд╛рде рдкреАрдбреАрдПрдл рдХреА рдкреАрдврд╝реА рдХрд╛ рдХрд╛рд░рдг рдмрдиреЗрдВрдЧреЗред
main = bracket ( newLogger Console ) closeLogger $ \ l -> do <br>
n <- comics<br>
writeLogger l Info $ "Number of comics to download: " ++ show n<br>
ch <- newChan<br>
mapM_ ( fork . retrieve ch l ) [ 1 .. n ] <br>
cts <- fmap ( take n ) $ getChanContents ch<br>
let imgs = catMaybes $ mapMaybe ( `lookup` cts ) [ 1 .. n ] <br>
pdf imgs `onException` ( writeLogger l Error "Unable to generate PDF" ) <br>
writeLogger l Info "PDF generated." <br>
bracket
рдлрд╝рдВрдХреНрд╢рди рдХрд╛
using
рдХрд░рдирд╛ рд╕рдорд╛рди рд╣реИ, рдпрд╣ рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░рдирд╛ рдХрд┐ рд▓реЙрдЧ рдмрдВрдж рд╣реИред
mapM_ ( fork . retrieve ch l ) [ 1 .. n ] <br>
mapM_ ( fork . retrieve ch l ) [ 1 .. n ] <br>
рд╣рдо рдкреНрд░рддреНрдпреЗрдХ рдирдВрдмрд░ рдХреЗ рд▓рд┐рдП рдПрдХ рдбрд╛рдЙрдирд▓реЛрдб рд╕реНрдЯреНрд░реАрдо рдмрдирд╛рддреЗ рд╣реИрдВ, рдЕрд░реНрдерд╛рдд рдХреЙрд▓
retrieve ch li
рдХрд░реЗрдВ
retrieve ch li
рдПрдХ рдЕрд▓рдЧ рдереНрд░реЗрдб рдореЗрдВред
fmap ( take n ) $ getChanContents ch<br>
рдкрд╣рд▓реЗ
n
рдкрд░рд┐рдгрд╛рдореЛрдВ рдХреЗ рд╕рд╛рде рд╣рдореЗрдВ рдПрдХ рдЖрд▓рд╕реА рд╕реВрдЪреА рд▓реМрдЯрд╛рдПрдЧрд╛ред рд▓реАрдЬрд┐рдП рдкреВрд░реА рд╕реВрдЪреА рдХрд╛ рдХреЛрдИ рдорддрд▓рдм рдирд╣реАрдВ рд╣реИ, рдХреНрдпреЛрдВрдХрд┐ рдЪреИрдирд▓ рдЕрдВрддрд╣реАрди рд╣реИред
рдлрд┐рд░ рд╣рдо рдХреНрд░рдо рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ рд╕реВрдЪрдХрд╛рдВрдХ рдХреЗ рд╕рд╛рде,
1
рд╕реЗ
n
рддрдХ
lookup
рдлрд╝рдВрдХреНрд╢рди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВред рдпрд╣ рдПрдХ рдЖрд▓рд╕реА рд╕реВрдЪреА рдХреЗ рд░реВрдк рдореЗрдВ рдЕрдЪреНрдЫреА рддрд░рд╣ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЖрд╡рд╢реНрдпрдХ рд╣реИ, рд▓реЗрдХрд┐рди рдЬрд┐рд╕рдореЗрдВ рдЪрд┐рддреНрд░ рдХреНрд░рдо рдореЗрдВ рд╕рдЦреНрддреА рд╕реЗ рдЪрд▓рддреЗ рд╣реИрдВред рдЗрд╕ рдкреНрд░рдХрд╛рд░, рд╣рдо рд╣рдореЗрд╢рд╛ рд╕рд╣реА рдХреНрд░рдо рдореЗрдВ рдЪрд┐рддреНрд░ рд▓рд┐рдЦреЗрдВрдЧреЗред
рдкреВрд░реА рд▓рд┐рд╕реНрдЯрд┐рдВрдЧ
main = bracket ( newLogger Console ) closeLogger $ \ l -> do <br>
n <- comics<br>
writeLogger l Info $ "Number of comics to download: " ++ show n<br>
ch <- newChan<br>
mapM_ ( fork . retrieve ch l ) [ 1 .. n ] <br>
cts <- fmap ( take n ) $ getChanContents ch<br>
let imgs = catMaybes $ mapMaybe ( `lookup` cts ) [ 1 .. n ] <br>
pdf imgs `onException` ( writeLogger l Error "Unable to generate PDF" ) <br>
writeLogger l Info "PDF generated." <br>
<br>
retrieve ch li = tryGet `onException` onFail where <br>
onFail = do <br>
writeChan ch ( i , Nothing ) <br>
writeLogger l Error $ "Comic " ++ show i ++ " failed to download" <br>
tryGet = do <br>
( imgUrl , title ) <- comic i<br>
imgData <- image imgUrl<br>
jpg <- writeBinaryFile fname imgData >> readJpegFile fname >>= either ( throwIO . userError . show ) return<br>
writeChan ch ( i , Just ( jpg , title ) ) <br>
writeLogger l Info $ "Comic " ++ show i ++ " downloaded" <br>
fname = show i ++ ".jpg" <br>
<br>
pdf imgs = runPdf "Xkcd.pdf" doc ( PDFRect 0 0 800 600 ) $ forM_ imgs genPage where <br>
genPage ( jpeg , title ) = do <br>
img <- createPDFJpeg jpeg<br>
page <- addPage Nothing <br>
drawWithPage page ( drawText ( text ( PDFFont Times_Roman 12 ) 0 0 ( toPDFString title ) ) >> drawXObject img ) <br>
doc = PDFDocumentInfo { <br>
author = toPDFString "voidex" , <br>
subject = toPDFString "xkcd" , <br>
pageMode = UseNone , <br>
pageLayout = OneColumn , <br>
viewerPreferences = standardViewerPrefs , <br>
compressed = False } <br>
<br>
rethrow :: ( Show e ) => Exceptional ea -> IO a<br>
rethrow = switch ( throwIO . userError . show ) return<br>
<br>
jsonAt url = simpleHTTP ( getRequest url ) >>= getResponseBody >>= rethrow . decode_ json<br>
<br>
str s = member_ ( literal s ) JSON . string<br>
num n = member_ ( literal n ) JSON . number<br>
<br>
comics = jsonAt "http://xkcd.com/info.0.json" >>= fmap ( fromIntegral . numerator ) . rethrow . decode_ ( num "num" ) <br>
<br>
comic n = jsonAt ( concat [ "http://xkcd.com/" , show n , "/info.0.json" ] ) >>= rethrow . decode_ ( str "img" <&> str "title" ) <br>
<br>
image url = simpleHTTP ( getRequest url ) >>= getResponseBody<br>
<br>
writeBinaryFile fname str = withBinaryFile fname WriteMode ( \ h -> hPutStr h str ) <br>
рджреБрд░реНрд╡рдЪрди
рджреБрд░реНрднрд╛рдЧреНрдп рд╕реЗ, рдкреАрдбреАрдПрдл рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдПрдХ рд╕рднреНрдп рдкреБрд╕реНрддрдХрд╛рд▓рдп рдХреА рдХрдореА рдХреЗ рдХрд╛рд░рдг, рдкрд░рд┐рдгрд╛рдо рдХрд╛ рднреБрдЧрддрд╛рди рдирд╣реАрдВ рд╣реБрдЖред
рдЕрдзрд┐рдХрд╛рдВрд╢ рдЫрд╡рд┐рдпрд╛рдВ рдПрдЪрдкреАрдбреАрдПрдл рд╕реНрд╡реАрдХрд╛рд░ рдХрд░рдиреЗ рд╕реЗ рдЗрдирдХрд╛рд░ рдХрд░рддреА рд╣реИрдВ (рдЬреЗрдкреАрдИрдЬреА рдЕрдкрд▓реЛрдб рдХреЗ рдПрдХ рдФрд░ рд╕рд╛рдЗрдХрд┐рд▓ рдХрд╛рд░реНрдпрд╛рдиреНрд╡рдпрди рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рдж), рдореБрдЭреЗ рдЫрд╡рд┐рдпреЛрдВ рдХреА рд╕реНрдХреЗрд▓рд┐рдВрдЧ рднреА рд╕рдордЭ рдореЗрдВ рдирд╣реАрдВ рдЖрдИред
рдкреНрд░рд╢рдВрд╕рд╛
рдЬреАрдПрдЪрд╕реАрдЖрдИ рдореЗрдВ рд╕реАрдзреЗ рдЕрдиреБрд░реЛрдз рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░рдирд╛ рдмрд╣реБрдд рд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рдерд╛, рдлрд┐рд░ рдЙрдирдореЗрдВ рд╕реЗ рдПрдХ рдХреЛ рдкрд╛рд░реНрд╕ рдХрд░реЗрдВ, рдЪрд┐рддреНрд░ рдХреЛ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░реЗрдВ рдФрд░ рд╕рд╣реЗрдЬреЗрдВред рд╕рд╛рд░рд╛ рд╡рд┐рдХрд╛рд╕ рд╡рд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛, рдФрд░ рдлрд┐рд░ рдХреЛрдб рдХреЛ рдПрдХ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ред рдорд▓реНрдЯреАрдлрд╝реНрд░реЗрдбрд┐рдВрдЧ рдХреЛ рдЗрдВрдЯрд░рдлреЗрд╕ рдпрд╛ рдХрд┐рд╕реА рдЕрддрд┐рд░рд┐рдХреНрдд рдХреЛрдб рдХреЛ рдЬреЛрдбрд╝рдиреЗ рдХреЗ рдмрд┐рдирд╛ рдЦрд░рд╛рдм рдХрд░ рджрд┐рдпрд╛ рдЧрдпрд╛ рдерд╛ред рдкрд░рд┐рдгрд╛рдо рдХреЛ рд╡рд╛рдкрд╕ рдХрд░рдиреЗ рдХреЗ рдмрдЬрд╛рдп, рд╣рдо рдмрд╕ рдЗрд╕реЗ рдЪреИрдирд▓ рдкрд░ рд▓рд┐рдЦрддреЗ рд╣реИрдВ, рдЬрд┐рд╕рдХреЗ рджреВрд╕рд░реЗ рдЫреЛрд░ рдкрд░ рдПрдХ рд╣реИрдВрдбрд▓рд░ рд╣реИред рдФрд░ рд╣рдо рдПрд╕рд┐рдВрдХреНрд░реЛрдирд╕ рдлрд╝рдВрдХреНрд╢рди рдореЗрдВ
fork
рдЬреЛрдбрд╝рддреЗ рд╣реИрдВред рд╕рд╛рдорд╛рдиреНрдп рдорд╛рдорд▓реЗ рдореЗрдВ, рд╕рдм рдХреБрдЫ рдЗрддрдирд╛ рд╕рд░рд▓ рдирд╣реАрдВ рд╣реИ, рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ, рд▓реЗрдХрд┐рди рдЕрдкрдиреЗ рд╕реНрд╡рдпрдВ рдХреЗ рдЕрдиреБрднрд╡ рд╕реЗ рдореИрдВ рдХрд╣реВрдВрдЧрд╛ рдХрд┐ рдореБрдЭреЗ рдЗрд╕рдХреЗ рд▓рд┐рдП рд╡рд╛рд╕реНрддреБрдХрд▓рд╛ рдХреЛ рдмрджрд▓рдирд╛ рдХрднреА рдирд╣реАрдВ рдкрдбрд╝рд╛ред
рд╕рд╛рдорд╛рдиреНрдп рддреМрд░ рдкрд░, hackage.haskell.org рдХреЛ рджреЗрдЦреЗрдВ , рдЖрд╡рд╢реНрдпрдХ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рджреЗрдЦреЗрдВ, рдФрд░ рдпрджрд┐ рдЖрдк рдкрд╛рддреЗ рд╣реИрдВ, рддреЛ рд╣рд╛рд╕реНрдХреЗрд▓ рдореЗрдВ рд╕рдм рдХреБрдЫ рд▓рд┐рдЦрдиреЗ рдХрд╛ рдореМрдХрд╛ рди рдЪреВрдХреЗрдВ!