Haskell
From Ggl's wiki
Contents |
What is Haskell
Features
Haskell is a pure functional programming language. Its main features are:
- purity (no side-effects)
- high-order functions
- lazy evaluation: a expression is evaluated only when needed
- strongly typed with type inference
- polymorphism
- currying
- pattern matching
- list comprehension
- monads
- type classes
- layout parsing: code blocks can be delimited by the layout (tab spaces)
Functions
composition: rotate = flipV . flipH forward composition: rotate = flipH >.> flipV
lambda: \x y = y*y
partial application:
sum x y = x + y sum 1 -> 1 + y
Lists
(head:tail) commonly written (x:xs)
List comprehension: [y | y <- xs, y <= x]
Useful functions: map, filter, foldr1, foldr, zip, zipWith
Short Examples
Some examples could be find there.
module Main where import System.IO import Char
Define some custom types. Simple types:
type Person = String type Book = String
List [] of (,) tuples (here pairs):
type LoanDB = [(Person, Book)]
Define the function that build the exemple database list:
exampleDB :: LoanDB
exampleDB = [("Alice", "Tintin"),
("Anna", "Little Women"),
("Alice", "Asterix"),
("Rory", "Tintin")]
Retrieve books from LoadDB db that were loaned by Person borrower. It uses a list comprehension where a book is in the returned list, if the person in the tuple (person, book) from db is the borrower:
books :: LoanDB -> Person -> [Book]
books db borrower
= [ book | (person, book) <- db, person==borrower]
Prepend the tuple (borrower, book) to db. ++ is the concatenation operator.
makeLoan :: LoanDB -> Person -> Book -> LoanDB makeLoan db borrower book = [(borrower, book)] ++ db
Remove (borrower, book) from db. Actually, it uses a list comprehension to return a list where all but (borrower, book) tuples are.
returnLoan :: LoanDB -> Person -> Book -> LoanDB returnLoan db borrower book = [(bwer, bk) | (bwer, bk) <- db, (bwer, bk) /= (borrower, book)]
Return a list of people who borrowed book:
borrowers :: LoanDB -> Book -> [Person]
borrowers db book
= [borrower | (borrower, bk) <- db, bk==book]
Tell if book was borrowed. Return True is there is at least one borrower e.g. length(borrowers db book) > 0.
borrowed :: LoanDB -> Book -> Bool
borrowed db book
= if length(borrowers db book) > 0
then True
else False
How many books did borrower borrow:
numBorrowed :: LoanDB -> Person -> Int
numBorrowed db borrower
= length(books db borrower)
In a list (x:xs), x is the head and xs the tail. It's often used in list recursion.
Pattern matching:
sum :: [Int] -> Int sum [] = 0 sum (x:xs) = x + sum xs
firstDigit st
= case (digits st) of
[] ->'\O'
(x:_) -> X
Guards:
comparison x y
| x < y = "The first is less"
| x > y = "The second is less"
| otherwise = "They are equal"
Insertion in an ordered list:
ins :: Int -> [Int] -> [Int] ins x [] = [x] ins x (y:ys) = | x <= y = x:(y:ys) | otherwise = y : ins x ys
Quicksort:
qSort :: [Int] -> [Int] qSort [] = [] qSort (x:xs) = qSort [y | y <- xs, y <= x] ++ [x] ++ qSort [y | y <- xs, y > x]
Sample Programs
Fastcgi configuration
With nginx, add this to your /etc/nginx/sites-enabled/<vhost> (might be in /etc/nginx/sites-enabled/default):
server {
listen 80;
[...]
location /hs {
include fastcgi_params;
fastcgi_pass localhost:9000;
fastcgi_index index.hs;
}
[...]
}
You should also have the file /etc/nginx/fastcgi_params (by default in debian package) with these values:
fastcgi_param QUERY_STRING $query_string; fastcgi_param REQUEST_METHOD $request_method; fastcgi_param CONTENT_TYPE $content_type; fastcgi_param CONTENT_LENGTH $content_length; fastcgi_param SCRIPT_NAME $fastcgi_script_name; fastcgi_param REQUEST_URI $request_uri; fastcgi_param DOCUMENT_URI $document_uri; fastcgi_param DOCUMENT_ROOT $document_root; fastcgi_param SERVER_PROTOCOL $server_protocol; fastcgi_param GATEWAY_INTERFACE CGI/1.1; fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; fastcgi_param REMOTE_ADDR $remote_addr; fastcgi_param REMOTE_PORT $remote_port; fastcgi_param SERVER_ADDR $server_addr; fastcgi_param SERVER_PORT $server_port; fastcgi_param SERVER_NAME $server_name;
Then when you go to http://localhost/hs/index.hs the fastcgi handler will connect to the fastcgi process on tcp port 9000. The fastcgi process may listen to a unix socket for better performance.
Feeds fetcher
module Main where
import Control.Applicative ((<*>))
import System.Environment ( getArgs )
import Data.List ( sortBy )
import Data.Ord ( comparing )
import Data.Maybe ( fromJust )
import Network.URI ( parseURI )
import Network.HTTP.Base
import Network.HTTP ( Request, simpleHTTP, rspBody )
import Text.Feed.Types ( Feed, Item )
import Text.Feed.Import
import Text.Feed.Query
import System.Locale ( defaultTimeLocale )
import System.Time ( formatCalendarTime )
import System.Time.Parse ( parseCalendarTime )
-- |A configuration has the format: "uri [name]"
parseConfigLine :: String -> (String, String)
parseConfigLine l = (uri l, name l)
where
uri = takeWhile (/= ' ')
name = takeWhile (/= ']') . tail . dropWhile (/= '[')
parseConfigString :: String -> [(String, String)]
parseConfigString = map parseConfigLine . lines
parseConfigPath :: FilePath -> IO [(String, String)]
parseConfigPath = fmap parseConfigString . readFile
getFeed :: (String, String) -> IO Feed
getFeed (uri, name) =
simpleHTTP (Request uri' GET [] "") >>= \(Right x) ->
return . fromJust . parseFeedString $ rspBody x
where
Just uri' = parseURI uri
getFeeds :: [(String, String)] -> IO [Feed]
getFeeds = mapM getFeed
itemTitle = fromJust . getItemTitle
itemDate = fromJust . getItemDate
itemLink = fromJust . getItemLink
-- "Wed, 9 Dec 2009 16:49:31 +0000"
dateFormat = "%a, %d %b %Y %H:%M:%S %Z"
convertDate = fromJust . parseCalendarTime defaultTimeLocale dateFormat
-- "2009/12/09 16:49 UTC"
showDate = formatCalendarTime defaultTimeLocale "%Y/%m/%d %R %Z"
itemFormat "date" item = Just $ showDate $ convertDate $ itemDate item
itemFormat "uri" item = Just $ concat ["\tURI: ", itemLink item]
itemFormat _ item = Nothing
showItem :: (Item, String) -> [String] -> String
showItem (i, ft) fmt = concat $ (["[" ++ ft ++ "- "]) ++
(map (\x -> case itemFormat x i of
Nothing -> ""
Just f -> f) fmt) ++ ["] "] ++ [itemTitle i]
main :: IO ()
main = do
feeds <- getArgs >>= parseConfigPath . head >>= getFeeds
let items_sorted = sortBy (\(i1, _) (i2, _) ->
comparing (convertDate . itemDate) i1 i2) (items feeds)
putStrLn $ unlines $ map (flip showItem ["date"]) $ items_sorted
where
items = concatMap (\x ->
map (\y -> (y, getFeedTitle x)) (getFeedItems x))
Del.icio.us example
A simple example of haskell delicious module usage to retrieve the last 10 links.
import Network.Delicious
user = User { userName = "user", userPass = "password" }
posts = withUser user (getAll Nothing)
posts' <- runDM user posts
take 10 . map postDesc $ posts'

