|
|
| (41 intermediate revisions not shown.) |
| Line 1: |
Line 1: |
| - | [[Category:How to]]
| + | == Haskell Cookbook == |
| - | {{Template:Anonymousdraft}}
| + | * [[Cookbook/Compilers and interpreters|Haskell compilers and interpreters]] |
| - | | + | |
| - | '''We need to start a Haskell centered cookbook (aka, not a [http://pleac.sourceforge.net/ PLEAC] clone)
| + | |
| - | | + | |
| - | This page is based on the Scheme Cookbook at
| + | |
| - | http://schemecookbook.org/Cookbook/WebHome'''
| + | |
| - | == Prelude == | + | |
| - | | + | |
| - | A lot of functions are defined in the "[http://www.haskell.org/hoogle/?q=Prelude Prelude]". Also, if you ever want to search for a function, based on the name, type or module, take a look at the excellent [http://www.haskell.org/hoogle/ Hoogle]. This is for a lot of people a must-have while debugging and writing Haskell programs.
| + | |
| - | | + | |
| - | == GHCi/Hugs == | + | |
| - | === GHCi interaction ===
| + | |
| - | To start GHCi from a command prompt, simply type `ghci'
| + | |
| - | | + | |
| - | $ ghci
| + | |
| - | ___ ___ _
| + | |
| - | / _ \ /\ /\/ __(_)
| + | |
| - | / /_\// /_/ / / | | GHC Interactive, version 6.6, for Haskell 98.
| + | |
| - | / /_\\/ __ / /___| | http://www.haskell.org/ghc/
| + | |
| - | \____/\/ /_/\____/|_| Type :? for help.
| + | |
| - |
| + | |
| - | Loading package base ... linking ... done.
| + | |
| - | Prelude>
| + | |
| - | | + | |
| - | [http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html Prelude] is the "base" library of Haskell.
| + | |
| - | | + | |
| - | To create variables at the GHCi prompt, use `let'
| + | |
| - | <haskell>
| + | |
| - | Prelude> let x = 5
| + | |
| - | Prelude> x
| + | |
| - | 5
| + | |
| - | Prelude> let y = 3
| + | |
| - | Prelude> y
| + | |
| - | 3
| + | |
| - | Prelude> x + y
| + | |
| - | 8
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | `let' is also the way to create simple functions at the GHCi prompt
| + | |
| - | <haskell>
| + | |
| - | Prelude> let fact n = product [1..n]
| + | |
| - | Prelude> fact 5
| + | |
| - | 120
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | | + | |
| - | === Checking Types ===
| + | |
| - | To check the type of an expression or function, use the command `:t'
| + | |
| - | <haskell>
| + | |
| - | Prelude> :t x
| + | |
| - | x :: Integer
| + | |
| - | Prelude> :t "Hello"
| + | |
| - | "Hello" :: [Char]
| + | |
| - | </haskell>
| + | |
| - | Haskell has the following types defined in the [http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html Standard Prelude].
| + | |
| - | <haskell>
| + | |
| - | Int -- bounded, word-sized integers
| + | |
| - | Integer -- unbounded integers
| + | |
| - | Double -- floating point values
| + | |
| - | Char -- characters
| + | |
| - | String -- equivalent to [Char], strings are lists of characters
| + | |
| - | () -- the unit type
| + | |
| - | Bool -- booleans
| + | |
| - | [a] -- lists
| + | |
| - | (a,b) -- tuples / product types
| + | |
| - | Either a b -- sum types
| + | |
| - | Maybe a -- optional values
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | * [[Cookbook/Strings|Strings]] | + | |
| | * [[Cookbook/Numbers|Numbers]] | | * [[Cookbook/Numbers|Numbers]] |
| | + | * [[Cookbook/Lists and strings|Lists and strings]] |
| | + | * [[Cookbook/Other data structures|Other data structures]] |
| | * [[Cookbook/Dates And Time|Dates and time]] | | * [[Cookbook/Dates And Time|Dates and time]] |
| | + | * [[Cookbook/Pattern matching|Pattern matching]] |
| | + | * [[Cookbook/Interactivity|Interactivity]] |
| | + | * [[Cookbook/Files|Files]] |
| | + | * [[Cookbook/Network programming|Network programming]] |
| | + | * [[Cookbook/XML|XML]] |
| | + | * [[Cookbook/Databases access|Databases access]] |
| | + | * [[Cookbook/Graphical user interfaces|Graphical user interfaces]] |
| | + | * [[Cookbook/PDF files|PDF files]] |
| | + | * [[Cookbook/FFI|FFI]] |
| | + | * [[Cookbook/Testing|Testing]] |
| | | | |
| - | == Lists == | + | == Similar projects for other programming languages == |
| - | In Haskell, lists are what Arrays are in most other languages. Haskell has all of the general list manipulation functions, see also <hask>Data.List</hask>.
| + | * [http://cl-cookbook.sourceforge.net/ Common Lisp Cookbook] |
| - | | + | * [http://pleac.sourceforge.net/ PLEAC] |
| - | <haskell>
| + | * [http://www.zenspider.com/Languages/Ruby/Cookbook/index.html Ruby Cookbook] |
| - | head [1,2,3] --> 1
| + | * [http://schemecookbook.org/Cookbook/WebHome Scheme Cookbook] |
| - | tail [1,2,3] --> [2,3]
| + | * [http://fssnip.net/ F# Snippets] |
| - | length [1,2,3] --> 3
| + | [[Category:FAQ]] |
| - | init [1,2,3] --> [1,2]
| + | [[Category:How to]] |
| - | last [1,2,3] --> 3
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | Furthermore, Haskell supports some neat concepts.
| + | |
| - | | + | |
| - | ===Infinite lists===
| + | |
| - | <haskell>
| + | |
| - | Prelude> [1..]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | The list of all squares:
| + | |
| - | <haskell>
| + | |
| - | square x = x*x
| + | |
| - | squares = map square [1..]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | But in the end, you probably don't want to use infinite lists, but make them finite. You can do this with <hask>take</hask>:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | Prelude> take 10 squares
| + | |
| - | [1,4,9,16,25,36,49,64,81,100]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | ===List comprehensions===
| + | |
| - | | + | |
| - | The list of all squares can also be written in a more comprehensive way, using list comprehensions:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | squares = [x*x | x <- [1..]]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | List comprehensions allow for constraints as well:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | -- multiples of 3 or 5
| + | |
| - | mults = [ x | x <- [1..], mod x 3 == 0 || mod x 5 == 0 ]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | == Other data structures ==
| + | |
| - | | + | |
| - | GHC comes with some handy data-structures by default. If you want to use a Map, use [http://hackage.haskell.org/packages/archive/containers/latest/doc/html/Data-Map.html Data.Map]. For sets, you can use Data.Set. A good way to find efficient data-structures is to take a look at the hierarchical libraries, see [http://haskell.org/ghc/docs/latest/html/libraries/index.html Haskell Hierarchical Libraries] and scroll down to 'Data'.
| + | |
| - | | + | |
| - | === Map ===
| + | |
| - | | + | |
| - | A naive implementation of a map would be using a list of tuples in the form of (key, value). This is used a lot, but has the big disadvantage that most operations take O(n) time.
| + | |
| - | | + | |
| - | Using [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Map.html Data.Map] we can construct a fast map using this data-structure:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | import qualified Data.Map as Map
| + | |
| - | | + | |
| - | myMap :: Map.Map String Int
| + | |
| - | myMap = Map.fromList [("alice", 111), ("bob", 333), ("douglas", 42)]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | We can then do quick lookups:
| + | |
| - | <haskell>
| + | |
| - | bobsPhone :: Maybe Int
| + | |
| - | bobsPhone = Map.lookup "bob" myMap
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | Map is often imported <hask>qualified</hask> to avoid name-clashing with the Prelude. See [[Import]] for more information.
| + | |
| - | | + | |
| - | === Set ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | === Tree ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | === ByteString ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | === Arrays ===
| + | |
| - | Arrays are generally eschewed in Haskell. However, they are useful if you desperately need constant lookup or update or if you have huge amounts of raw data.
| + | |
| - | | + | |
| - | [http://hackage.haskell.org/packages/archive/array/latest/doc/html/Data-Array-IArray.html Immutable arrays] like <hask>Data.Array.IArray.Array i e</hask> offer lookup in constant time but they get copied when you update an element. Use them if they can be filled in one go.
| + | |
| - | The following example groups a list of numbers according to their residual after division by <hask>n</hask> in one go.
| + | |
| - | <haskell>
| + | |
| - | bucketByResidual :: Int -> [Int] -> Array Int [Int]
| + | |
| - | bucketByResidual n xs = accumArray (\xs x -> x:xs) [] (0,n-1) [(x `mod` n, x) | x <- xs]
| + | |
| - | | + | |
| - | Data.Arra.IArray> bucketByResidual 4 [x*x | x <- [1..10]]
| + | |
| - | array (0,3) [(0,[100,64,36,16,4]),(1,[81,49,25,9,1]),(2,[]),(3,[])]
| + | |
| - | | + | |
| - | Data.Arra.IArray> amap reverse it
| + | |
| - | array (0,3) [(0,[4,16,36,64,100]),(1,[1,9,25,49,81]),(2,[]),(3,[])]
| + | |
| - | </haskell>
| + | |
| - | Note that the array can fill itself up in a circular fashion. Useful for dynamic programming. Here is the [[Edit distance]] between two strings without array updates.
| + | |
| - | <haskell>
| + | |
| - | editDistance :: Eq a => [a] -> [a] -> Int
| + | |
| - | editDistance xs ys = table ! (m,n)
| + | |
| - | where
| + | |
| - | (m,n) = (length xs, length ys)
| + | |
| - | x = array (1,m) (zip [1..] xs)
| + | |
| - | y = array (1,n) (zip [1..] ys)
| + | |
| - |
| + | |
| - | table :: Array (Int,Int) Int
| + | |
| - | table = array bnds [(ij, dist ij) | ij <- range bnds]
| + | |
| - | bnds = ((0,0),(m,n))
| + | |
| - |
| + | |
| - | dist (0,j) = j
| + | |
| - | dist (i,0) = i
| + | |
| - | dist (i,j) = minimum [table ! (i-1,j) + 1, table ! (i,j-1) + 1,
| + | |
| - | if x ! i == y ! j then table ! (i-1,j-1) else 1 + table ! (i-1,j-1)]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | | + | |
| - | [http://hackage.haskell.org/packages/archive/array/latest/doc/html/Data-Array-MArray.html Mutable arrays] like <hask>Data.Array.IO.IOArray i e</hask> are updated in place, but they have to live in the IO-monad or the ST-monad in order to not destroy referential transparency. There are also [http://hackage.haskell.org/packages/archive/array/latest/doc/html/Data-Array-Diff.html diff arrays] like <hask>Data.Array.Diff.DiffArray i e</hask> that look like immutable arrays but do updates in place if used in a single threaded way. Here is depth first search with diff arrays that checks whether a directed graph contains a cycle. ''Note: this example really belongs to Map or Set.''
| + | |
| - | <haskell>
| + | |
| - | import Control.Monad.State
| + | |
| - | type Node = Int
| + | |
| - | data Color = White | Grey | Black
| + | |
| - | | + | |
| - | hasCycle :: Array Node [Node] -> Bool
| + | |
| - | hasCycle graph = runState (mapDfs $ indices g) initSeen
| + | |
| - | where
| + | |
| - | initSeen :: DiffArray Node Color
| + | |
| - | initSeen = listArray (bounds graph) (repeat White)
| + | |
| - | mapDfs = fmap or . mapM dfs
| + | |
| - | dfs node = get >>= \seen -> case (seen ! node) of
| + | |
| - | Black -> return False
| + | |
| - | Grey -> return True -- we found a cycle
| + | |
| - | White -> do
| + | |
| - | modify $ \seen -> seen // [(node,Grey )]
| + | |
| - | found <- mapDfs (graph ! node)
| + | |
| - | modify $ \seen -> seen // [(node,Black)]
| + | |
| - | return found
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | == Pattern matching ==
| + | |
| - | | + | |
| - | Regular expressions are useful in some situations where the Data.List
| + | |
| - | library is unwieldy. Posix style regular expressions are available in
| + | |
| - | the core libraries, and a suite of other regular expression libraries
| + | |
| - | are [also available], including PCRE and TRE-style regexes.
| + | |
| - | | + | |
| - | Bryan O'Sullivan has written [http://www.serpentine.com/blog/2007/02/27/a-haskell-regular-expression-tutorial/ a nice introduction] to using the new regex libraries.
| + | |
| - | | + | |
| - | == Interactivity ==
| + | |
| - | | + | |
| - | === Reading a string ===
| + | |
| - | Strings can be read as input using [http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v%3AgetLine getLine].
| + | |
| - | <haskell>
| + | |
| - | Prelude> getLine
| + | |
| - | Foo bar baz
| + | |
| - | "Foo bar baz"
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | === Printing a string ===
| + | |
| - | Strings can be output in a number of different ways.
| + | |
| - | <haskell>
| + | |
| - | Prelude> putStr "Foo"
| + | |
| - | FooPrelude>
| + | |
| - | </haskell>
| + | |
| - | As you can see, [http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v%3AputStr putStr] does not include the newline character `\n'. We can either use putStr like this:
| + | |
| - | <haskell>
| + | |
| - | Prelude> putStr "Foo\n"
| + | |
| - | Foo
| + | |
| - | </haskell>
| + | |
| - | Or use [http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v%3AputStrLn putStrLn], which is already in the Standard Prelude
| + | |
| - | <haskell>
| + | |
| - | Prelude> putStrLn "Foo"
| + | |
| - | Foo
| + | |
| - | </haskell>
| + | |
| - | We can also use [http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v%3Aprint print] to print a string, '''including the quotation marks.'''
| + | |
| - | <haskell>
| + | |
| - | Prelude> print "Foo"
| + | |
| - | "Foo"
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | === Parsing command line arguments ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | == Files ==
| + | |
| - | | + | |
| - | === Reading from a file ===
| + | |
| - | The System.IO library contains the functions needed for file IO. The program
| + | |
| - | below displays the contents of the file c:\test.txt.
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | import System.IO
| + | |
| - | | + | |
| - | main = do
| + | |
| - | h <- openFile "c:\\test.txt" ReadMode
| + | |
| - | contents <- hGetContents h
| + | |
| - | putStrLn contents
| + | |
| - | hClose h
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | The same program, with some higher-lever functions:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | main = do
| + | |
| - | contents <- readFile "c:\\test.txt"
| + | |
| - | putStrLn contents
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | === Writing to a file ===
| + | |
| - | | + | |
| - | The following program writes the first 100 squares to a file:
| + | |
| - | <haskell>
| + | |
| - | -- generate a list of squares with length 'num' in string-format.
| + | |
| - | numbers num = unlines $ take num $ map (show . \x -> x*x) [1..]
| + | |
| - | | + | |
| - | main = do
| + | |
| - | writeFile "test.txt" (numbers 100)
| + | |
| - | putStrLn "successfully written"
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | This will override the old contents of the file, or create a new file if the file doesn't exist yet. If you want to append to a file, you can use <hask>appendFile</hask>.
| + | |
| - | | + | |
| - | === Creating a temporary file ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | === Writing a filter ===
| + | |
| - | Using [http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v:interact interact], you can easily do things with stdin and stdout.
| + | |
| - | | + | |
| - | A program to sum up numbers:
| + | |
| - | | + | |
| - | <haskell>main = interact $ show . sum . map read . lines</haskell>
| + | |
| - | | + | |
| - | A program that adds line numbers to each line:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | main = interact numberLines
| + | |
| - | numberLines = unlines . zipWith combine [1..] . lines
| + | |
| - | where combine lineNumber text = concat [show lineNumber, " ", text]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | === Logging to a file ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | == Network programming ==
| + | |
| - | The following example makes use of the Network and System.IO libraries to open
| + | |
| - | a socket connection to Google and retrieve the Google home page.
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | import Network;
| + | |
| - | import System.IO;
| + | |
| - |
| + | |
| - | main = withSocketsDo $ do
| + | |
| - | h <- connectTo "www.google.com" (PortNumber 80)
| + | |
| - | hSetBuffering h LineBuffering
| + | |
| - | hPutStr h "GET / HTTP/1.1\nhost: www.google.com\n\n"
| + | |
| - | contents <- hGetContents h
| + | |
| - | putStrLn contents
| + | |
| - | hClose h
| + | |
| - | </haskell>
| + | |
| - | == XML ==
| + | |
| - | === Libraries ===
| + | |
| - | There are multiple libraries available. In my own (limited) experience, I could only get [[HXT]] to do everything I wanted. It does make heavy use of [[http://haskell.org/arrows/ Arrows]].
| + | |
| - | | + | |
| - | === Parsing XML ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | == Databases access ==
| + | |
| - | There are two packages you can use to connect to MySQL, PostgreSQL, Sqlite3 and ODBC databases: [http://software.complete.org/software/projects/show/hdbc HDBC] and Hsql
| + | |
| - | | + | |
| - | === MySQL ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | === PostgreSQL ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | === SQLite ===
| + | |
| - | Suppose you have created a 'test.db' database like this,
| + | |
| - | | + | |
| - | $ sqlite3 test.db "create table t1 (t1key INTEGER PRIMARY KEY,data TEXT,num double,timeEnter DATE);"
| + | |
| - | | + | |
| - | $ sqlite3 test.db "insert into t1 (data,num) values ('This is sample data',3);"
| + | |
| - | | + | |
| - | $ sqlite3 test.db "insert into t1 (data,num) values ('More sample data',6);"
| + | |
| - | | + | |
| - | $ sqlite3 test.db "insert into t1 (data,num) values ('And a little more',9);"
| + | |
| - | | + | |
| - | Using HDBC and HDBC-sqlite3 packages, you can connect and query it like this:
| + | |
| - | <haskell>
| + | |
| - | import Control.Monad
| + | |
| - | import Database.HDBC
| + | |
| - | import Database.HDBC.Sqlite3
| + | |
| - | | + | |
| - | main = do conn <- connectSqlite3 "test.db"
| + | |
| - | rows <- quickQuery' conn "SELECT * from t1" []
| + | |
| - | forM_ rows $ \row -> putStrLn $ show row
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | | + | |
| - | $ ghc --make sqlite.hs
| + | |
| - | | + | |
| - | $ ./sqlite
| + | |
| - | | + | |
| - | output:
| + | |
| - | | + | |
| - | [SqlString "1",SqlString "This is sample data",SqlString "3.0",SqlNull]
| + | |
| - | | + | |
| - | [SqlString "2",SqlString "More sample data",SqlString "6.0",SqlNull]
| + | |
| - | | + | |
| - | [SqlString "3",SqlString "And a little more",SqlString "9.0",SqlNull]
| + | |
| - | | + | |
| - | == Graphical user interfaces ==
| + | |
| - | | + | |
| - | === wxHaskell ===
| + | |
| - | [[WxHaskell|wxHaskell]] is a portable and native GUI library for Haskell based on the wxWidgets Library.
| + | |
| - | | + | |
| - | Hello World example:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | module Main where
| + | |
| - | import Graphics.UI.WX
| + | |
| - | | + | |
| - | main :: IO ()
| + | |
| - | main
| + | |
| - | = start hello
| + | |
| - | | + | |
| - | hello :: IO ()
| + | |
| - | hello
| + | |
| - | = do f <- frame [text := "Hello!"]
| + | |
| - | quit <- button f [text := "Quit", on command := close f]
| + | |
| - | set f [layout := widget quit]
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | This code was taken from [[WxHaskell/Quick_start | "a quick start with wxHaskell"]].
| + | |
| - | | + | |
| - | === Gtk2Hs ===
| + | |
| - | [http://haskell.org/gtk2hs/screenshots/ Gtk2Hs] is a GUI Library for
| + | |
| - | Haskell based on GTK. [http://home.telfort.nl/sp969709/gtk2hs/ Gtk2Hs Tutorial].
| + | |
| - | | + | |
| - | Hello world example:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | import Graphics.UI.Gtk
| + | |
| - | | + | |
| - | main :: IO ()
| + | |
| - | main = do
| + | |
| - | initGUI
| + | |
| - | w <- windowNew
| + | |
| - | b <- buttonNew
| + | |
| - | set b [buttonLabel := "Quit"]
| + | |
| - | onClicked b $ widgetDestroy w
| + | |
| - | set w [windowTitle := "Hello", containerBorderWidth := 10]
| + | |
| - | containerAdd w b
| + | |
| - | onDestroy w mainQuit
| + | |
| - | widgetShowAll w
| + | |
| - | mainGUI
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | For more examples, see: [[Applications and libraries/Games]]
| + | |
| - | | + | |
| - | === HOpenGL ===
| + | |
| - | [http://www.haskell.org/HOpenGL/ HOpenGL] is a Haskell binding for the OpenGL graphics API (GL 1.2.1 / GLU 1.3) and the portable OpenGL utility toolkit GLUT.
| + | |
| - | There is a Haskell OpenGL Tetris program at
| + | |
| - | [[http://haskell-tetris.pbwiki.com/Main]] by Jim.
| + | |
| - | | + | |
| - | See also: [[Applications and libraries/Games]]
| + | |
| - | | + | |
| - | === SDL ===
| + | |
| - | There are some Haskell bindings to [http://libsdl.org/ SDL] at [http://hackage.haskell.org/packages/archive/pkg-list.html Hackage].
| + | |
| - | | + | |
| - | == PDF files ==
| + | |
| - | | + | |
| - | For the following recipes you need to install [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HPDF HPDF].
| + | |
| - | | + | |
| - | === Creating an empty PDF file ===
| + | |
| - | | + | |
| - | The following code creates an empty PDF file with the name "test1.pdf":
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | import Graphics.PDF
| + | |
| - | | + | |
| - | main :: IO ()
| + | |
| - | main = do
| + | |
| - | let outputFileName= "test1.pdf"
| + | |
| - | let defaultPageSize = PDFRect 0 0 200 300
| + | |
| - |
| + | |
| - | runPdf outputFileName standardDocInfo defaultPageSize $ do
| + | |
| - | addPage Nothing
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | === Pages with different sizes ===
| + | |
| - | | + | |
| - | If you pass "Nothing" to the function [http://hackage.haskell.org/packages/archive/HPDF/latest/doc/html/Graphics-PDF-Document.html#v%3AaddPage addPage], the default page size will be used for the size of the new page.
| + | |
| - | | + | |
| - | Let’s create three pages, the last two pages with different dimensions:
| + | |
| - | | + | |
| - | <haskell>
| + | |
| - | import Graphics.PDF
| + | |
| - | | + | |
| - | main :: IO ()
| + | |
| - | main = do
| + | |
| - | let outputFileName= "test2.pdf"
| + | |
| - | let defaultPageSize = PDFRect 0 0 200 300
| + | |
| - |
| + | |
| - | runPdf outputFileName standardDocInfo defaultPageSize $ do
| + | |
| - | addPage Nothing
| + | |
| - | addPage $ Just $ PDFRect 0 0 100 100
| + | |
| - | addPage $ Just $ PDFRect 0 0 150 150
| + | |
| - | </haskell>
| + | |
| - | | + | |
| - | == FFI ==
| + | |
| - | === How to interface with C===
| + | |
| - | | + | |
| - | Magnus has written [http://therning.org/magnus/archives/315 a nice example ] on how to call a C function operating on a user defined type.
| + | |
| - | | + | |
| - | == Testing ==
| + | |
| - | | + | |
| - | === QuickCheck ===
| + | |
| - | | + | |
| - | TODO
| + | |
| - | | + | |
| - | === HUnit ===
| + | |
| - | | + | |
| - | TODO
| + | |