fibonacci 0 = 0 fibonacci 1 = 1 fibonacci n = fibonacci (n - 1) + fibonacci (n - 2)
Every expression in Haskell has a type which is determined at compile time. All the types composed together by function application have to match up. If they don't, the program will be rejected by the compiler. Types become not only a form of guarantee, but a language for expressing the construction of programs.
All Haskell values have a type:
char = 'a' :: Char int = 123 :: Int fun = isDigit :: Char -> Bool
You have to pass the right type of values to functions, or the compiler will reject the program:
You can decode bytes into text:
bytes = Crypto.Hash.SHA1.hash "hello" :: ByteString text = decodeUtf8 bytes :: Text
But you cannot decode Text, which is already a vector of Unicode points:
doubleDecode = decodeUtf8 (decodeUtf8 bytes)
Every function in Haskell is a function in the mathematical sense (i.e., "pure"). Even side-effecting IO operations are but a description of what to do, produced by pure code. There are no statements or instructions, only expressions which cannot mutate variables (local or global) nor access state like time or random numbers.
The following function takes an integer and returns an integer. By the type it cannot do any side-effects whatsoever, it cannot mutate any of its arguments.
square :: Int -> Int square x = x * x
The following string concatenation is okay:
"Hello: " ++ "World!"
The following string concatenation is a type error:
"Name: " ++ getLine
getLine has type
IO String and not
"Name: " is. So by the type system you cannot mix and match purity with impurity.
You don't have to explicitly write out every type in a Haskell program. Types will be inferred by unifying every type bidirectionally. However, you can write out types if you choose, or ask the compiler to write them for you for handy documentation.
This example has a type signature for every binding:
main :: IO () main = do line :: String <- getLine print (parseDigit line) where parseDigit :: String -> Maybe Int parseDigit ((c :: Char) : _) = if isDigit c then Just (ord c - ord '0') else Nothing
But you can just write:
main = do line <- getLine print (parseDigit line) where parseDigit (c : _) = if isDigit c then Just (ord c - ord '0') else Nothing
You can also use inference to avoid wasting time explaining what you want:
do ss <- decode "[\"Hello!\",\"World!\"]" is <- decode "[1,2,3]" return (zipWith (\s i -> s ++ " " ++ show (i + 5)) ss is) => Just ["Hello! 6","World! 7"]
Types give a parser specification for free, the following input is not accepted:
do ss <- decode "[1,2,3]" is <- decode "[null,null,null]" return (zipWith (\s i -> s ++ " " ++ show (i + 5)) ss is) => Nothing
Haskell lends itself well to concurrent programming due to its explicit handling of effects. Its flagship compiler, GHC, comes with a high-performance parallel garbage collector and light-weight concurrency library containing a number of useful concurrency primitives and abstractions.
Easily launch threads and communicate with the standard library:
main = do done <- newEmptyMVar forkIO (do putStrLn "I'm one thread!" putMVar done "Done!") second <- forkIO (do delayThread 100000 putStrLn "I'm another thread!") killThread second msg <- takeMVar done putStrLn msg
Use an asynchronous API for threads:
do a1 <- async (getURL url1) a2 <- async (getURL url2) page1 <- wait a1 page2 <- wait a2 ...
Atomic threading with software transactional memory:
transfer :: Account -> Account -> Int -> IO () transfer from to amount = atomically (do deposit to amount withdraw from amount)
Atomic transactions must be repeatable, so arbitrary IO is disabled in in the type system:
main = atomically (putStrLn "Hello!")
Functions don't evaluate their arguments. This means that programs can compose together very well, with the ability to write control constructs (such as if/else) just by writing normal functions. The purity of Haskell code makes it easy to fuse chains of functions together, allowing for performance benefits.
Define control structures easily:
when p m = if p then m else return () main = do args <- getArgs when (null args) (putStrLn "No args specified!")
If you notice a repeated expression pattern, like
if c then t else False
you can give this a name, like
and c t = if c then t else False
and then use it with the same effect as the orginal expression.
Get code re-use by composing lazy functions. It's quite natural to express the
any function by reusing the
any :: (a -> Bool) -> [a] -> Bool any p = or . map p
Reuse the recursion patterns in
Open source contribution to Haskell is very active with a wide range of packages available on the public package servers.
There are 6,954 packages freely available, here is a sample of the most common ones:
|bytestring||Binary data||base||Prelude, IO, threads|
|hspec||RSpec-like tests||attoparsec||Fast parser|
|snap||Web framework||time||Date, time, etc.|
|happstack||Web framework||yesod||Web framework|
|containers||Maps, graphs, sets||fsnotify||Watch filesystem|
|hint||Interpret Haskell||unix||UNIX bindings|
|SDL||SDL binding||OpenGL||OpenGL graphics system|
|cairo||Cairo graphics||statistics||Statistical analysis|
|gtk||Gtk+ library||glib||GLib library|
|test-framework||Testing framework||resource-pool||Resource pooling|
|conduit||Streaming I/O||mwc-random||High-quality randoms|
|QuickCheck||Property testing||stm||Atomic threading|
|blaze-html||Markup generation||cereal||Binary parsing/printing|
|xml||XML parser/printer||http-client||HTTP client engine|
|warp||Web server||text-icu||Text encodings|
|pipes||Streaming IO||scientific||Arbitrary-prec. nums|
|process||Launch processes||aeson||JSON parser/printer|