Why Haskell?

Bob Ippolito

January 8, 2014


Want to learn more? Take my Intro to Haskell course at
enginehere.com/courses/intro-to-haskell-etrepum

Haskell's Appeal

  • Abstractions can often be used without penalty
  • Efficient parallel and concurrent programming
  • Type system makes maintenance easier
  • Nice syntax (not too heavy or lightweight)
  • Fantastic community & ecosystem

What do I know?

  • Took a class when I worked at Facebook in 2012
  • Ported the exercism.io curriculum to Haskell
  • I practice regularly and try to read Haskell often
  • I'm not an expert!

Hello world!


main = print "hello world"

With types


main :: IO ()
main = putStrLn "hello world"

You'll need a local Haskell interpreter for this due to input. You can replace getLine with return "some input" to try here.

Run with runhaskell WOPR.hs.


-- WOPR.hs

main :: IO ()
main = do
  putStrLn "WHAT GAME WOULD YOU LIKE TO PLAY?"
  game <- getLine
  if game == "GLOBAL THERMONUCLEAR WAR"
    then putStrLn "WOULDN'T YOU PREFER A NICE GAME OF CHESS?"
    else putStrLn "GOOD CHOICE."

Functions


circleArea :: Float -> Float
circleArea r = pi * r ^ 2

main :: IO ()
main = print (circleArea 10)

Functions


circleArea :: Float -> Float
circleArea = \r -> pi * r ^ 2

main :: IO ()
main = print (circleArea 10)

Multiple arguments


rectangleArea :: Float -> Float -> Float
rectangleArea w h = w * h

main :: IO ()
main = print (rectangleArea 10 40)

Multiple arguments


rectangleArea :: Float -> Float -> Float
rectangleArea = \w -> \h -> w * h

main :: IO ()
main = print (rectangleArea 10 40)

Pattern matching


myAnd :: Bool -> Bool -> Bool
myAnd True b = b
myAnd _    _ = False

main :: IO ()
main = print (myAnd (10 > 5) (100 * 2 == 200))

Pattern matching


myAnd :: Bool -> Bool -> Bool
myAnd = \a ->
  case a of
    True  -> \b -> b
    False -> \_ -> False

main :: IO ()
main = print (myAnd (10 > 5) (100 * 2 == 200))

Can you define xor?


myXor :: Bool -> Bool -> Bool
myXor a b = undefined

main :: IO ()
main = do
  -- these should all print True
  print (myXor True True == False)
  print (myXor True False == True)
  print (myXor False True == True)
  print (myXor False False == False)

Guards


fac :: Integer -> Integer
fac n
  | n > 1     = n * fac (n - 1)
  | otherwise = n

main :: IO ()
main = print (fac 26)

Operators


(.^) :: Bool -> Bool -> Bool
True .^ True = False
x    .^ y    = x || y

main :: IO ()
main = print (True .^ True)

Operators


(.^) :: Bool -> Bool -> Bool
(.^) True True = False
(.^) x    y    = x || y

main :: IO ()
main = print (True .^ True)

Operators


(.^) :: Bool -> Bool -> Bool
True .^ True = False
x    .^ y    = x || y

main :: IO ()
main = print ((.^) True True)

Operators


xor :: Bool -> Bool -> Bool
True `xor` True = False
x    `xor` y    = x || y

main :: IO ()
main = print (xor True True)

Operators


(.^) :: Bool -> Bool -> Bool
xor True True = False
xor x    y    = x || y

main :: IO ()
main = print (True `xor` True)

Haskell

Haskell B. Curry

Pre-History

1930s-1950s
Lambda Calculus (Turing)
Combinatory Calculus (Curry & Feys)
LISP (McCarthy)
1960s-1970s
Operational (Landin) and Denotational (Strachey) semantics
ML (Milner)
Lazy FP & Graph Reduction (Turner)
1980s
Miranda (Turner)
Lazy ML (Augustsson & Johnsson)

Early History

1987
More than a dozen non-strict FP languages in use
FCPA '87 meeting (Peyton Jones, Hudak, et. al.)
Formed FPLang committee
Wanted to base language on Miranda, but Turner declined
1988
Chose the name Haskell
Hudak and Wadler chosen to be editors of the report
1990 (April 1st!)
Haskell 1.0 report published (125 pages)
IFIP 1992 Working Group

Evolution

1992
Glasgow Haskell Compiler (GHC)
1996
Haskell 1.3 - Monadic I/O, seq, strictness annotations
1999
Haskell 98 - Commitment to stability
2002
Revised Haskell 98 (260 pages)

Fun Quotes

Tony Hoare (1990s?)
I fear that Haskell is doomed to succeed
Simon Peyton Jones (2003)
Motto: avoid success at all costs
Haskell: Batteries Included (2008)
Avoid "avoiding success"

Domain

General Purpose

  • Very effective for parsing and compilation
  • Great for DSEL (Domain Specific Embedded Languages)
  • Has been popular in academia for some time
  • Becoming more popular in industry

Commercial Use

Biotech
Amgen - informatics, simulation
Finance
Credit Suisse - quantitative modeling
Barclays - DSEL for exotic equity derivatives
Deutsche Bank - trading group infrastructure
Tsuru Capital - trading platform
McGraw-Hill Financial - report generation (with ermine)
Semiconductor Design
Bluespec - high-level language for chip design

Consumer Apps

Silk
"A platform for sharing collections about anything"
Chordify
"Chord transcription for the masses"
Bump (Google, Sep 2013)
"Send files, videos, everything!" Mobile + web.
MailRank (Facebook, Nov 2011)
Email inbox prioritization. Shuttered post-acquisition.
Bazqux
"RSS reader that shows comments to posts"

Commercial Services

janrain
User management platform.
AlephCloud
Content-centric security for cloud and mobile
Spaceport (Facebook, Aug 2013)
Mobile game framework using ActionScript 3
scrive
E-signing service (nordic market)
OpenBrain
Computing platform for scientific and business analytics
skedge.me
Enterprise appointment scheduling

Compilers

Haskell
GHC, Ajhc, Haste, GHCJS
Dependently typed
Agda - also an interactive proof assistant!
Idris - general purpose
Compile to JavaScript
Elm - functional reactive in the browser
Fay - Haskell subset
purescript - Haskell-like syntax, statically typed
Imperative
Pugs - first Perl 6 implementation

Standalone Apps

Pandoc
Markup swiss-army knife (used to make these slides!)
Darcs
Distributed revision control system (like Git or Mercurial)
xmonad
"the tiling window manager that rocks"
Gitit
Wiki backed by Git, Darcs, or Mercurial
git-annex
Manage large files with git (similar to Dropbox)
ImplicitCAD
Programmatic Solid 3D CAD modeler

Haskell Platform

Haskell: Batteries Included

Editor Support

Emacs
ghc-mod + HLint
Vim
hdevtools + Syntastic + vim-hdevtools
Sublime Text
hdevtools + SublimeHaskell
Eclipse
EclipseFP + HLint
Web
FP Haskell Center

Haskell Syntax

Types
Defines types and typeclasses

Constructors and record accessors become values

Values
Named bindings
Instances of constructors
Functions
Control flow

Abstract Data Types

-- sum type, 3 possible values
data Choice = Definitely
            | Possibly
            | NoWay

-- product type, 9 possible values (3 * 3)
data Choices = Choices Choice Choice

-- record syntax defines accessors automatically
data Choices = Choices { fstChoice :: Choice
                       , sndChoice :: Choice
                       }

Time to model Rock Paper Scissors


data Move = Rock
          | Paper
          | Scissors
          deriving (Show, Eq)

data Outcome = Lose
             | Draw
             | Win
             deriving (Show, Eq)

score :: Move -> Move -> Outcome
score a b = undefined

main :: IO ()
main = do
  print (score Rock Paper)
  print (score Rock Scissors)

Implement shapeArea


data Shape = Circle Float
           | Rectangle Float Float
           deriving (Show, Eq)

shapeArea :: Shape -> Float
shapeArea _ = undefined

main :: IO ()
main = do
  print (shapeArea (Circle 10) == pi * 100)
  print (shapeArea (Rectangle 2 12) == 24)

Using Types

-- Bindings can be annotated
success :: a -> Maybe a
-- Constructors are functions
success = Just

-- Constructors can be pattern matched
-- _ is a wildcard
case success True of
  Just True -> ()
  _         -> ()

-- Values can be annotated in-line
2 ^ (1 :: Int)

Typeclass Syntax

class Equals a where
  isEqual :: a -> a -> Bool

instance Equals Choice where
  isEqual Definitely Definitely = True
  isEqual Possibly   Possibly   = True
  isEqual NoWay      NoWay      = True
  isEqual _          _          = False

instance (Equals a) => Equals [a] where
  isEqual (a:as) (b:bs) = isEqual a b &&
                          isEqual as bs
  isEqual as     bs     = null as && null bs

Bindings & Functions


greeting :: String
greeting = "Hello, "

sayHello :: String -> String
sayHello name = greeting ++ name
-- desugars to:
sayHello = \name -> (++) greeting name

sayHello name = result
  where result = greeting ++ name
-- desugars to:
sayHello = \name ->
  let result = (++) greeting name
  in result

Pattern Matching


-- Unlike Erlang, pattern matching is only on
-- constructors, never variables
isJust (Just _) = True
isJust Nothing  = False
-- desugars to:
isJust = \x ->
  case x of
    (Just _) -> True
    Nothing  -> False

Guards


isNegative :: (Num a) => a -> Bool
isNegative x
  | x < 0     = True
  | otherwise = False
-- desugars to:
isNegative = \x ->
  if (<) x 0
  then True
  else False

`Infix` and (Prefix)

-- Symbolic operators can be used
-- prefix when in (parentheses)
(+) a b

-- Named functions can be used
-- infix when in `backticks`
x `elem` xs

-- infixl, infixr define associativity
-- and precedence (0 lowest, 9 highest)
infixr 5 `append`
a `append` b = a ++ b

Do syntax


do m
-- desugars to:
m

do a <- m
   return a
-- desugars to:
m >>= \a -> return a

do m
   return ()
-- desugars to:
m >> return ()

Key Features

  • Interactive
  • Declarative
  • Pure
  • Non-strict (lazy) evaluation
  • Types and typeclasses
  • Abstractions
  • Multi-paradigm

GHCi

Interactive Haskell


$ runhaskell --help
Usage: runghc [runghc flags] [GHC flags] module [program args]

The runghc flags are
    -f /path/to/ghc       Tell runghc where GHC is
    --help                Print this usage information
    --version             Print version number

$ ghci
GHCi, version 7.6.3: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
h> 

:t shows type information


h> :t map
map :: (a -> b) -> [a] -> [b]
h> :t map (+1)
map (+1) :: Num b => [b] -> [b]
h> :t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b

:i shows typeclass info


h> :i Num
class Num a where
  (+) :: a -> a -> a
  (*) :: a -> a -> a
  (-) :: a -> a -> a
  negate :: a -> a
  abs :: a -> a
  signum :: a -> a
  fromInteger :: Integer -> a
    -- Defined in `GHC.Num'
instance Num Integer -- Defined in `GHC.Num'
instance Num Int -- Defined in `GHC.Num'
instance Num Float -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'

:i shows value info


h> :info map
map :: (a -> b) -> [a] -> [b]   
-- Defined in `GHC.Base'
h> :info (>>=)
class Monad m where
  (>>=) :: m a -> (a -> m b) -> m b
  ...
    -- Defined in `GHC.Base'
infixl 1 >>=

:i shows type info


h> :info Int
data Int = ghc-prim:GHC.Types.I#
  ghc-prim:GHC.Prim.Int#
  -- Defined in `ghc-prim:GHC.Types'
instance Bounded Int -- Defined in `GHC.Enum'
instance Enum Int -- Defined in `GHC.Enum'
instance Eq Int -- Defined in `GHC.Classes'
instance Integral Int -- Defined in `GHC.Real'
instance Num Int -- Defined in `GHC.Num'
instance Ord Int -- Defined in `GHC.Classes'
instance Read Int -- Defined in `GHC.Read'
instance Real Int -- Defined in `GHC.Real'
instance Show Int -- Defined in `GHC.Show'

:l load a module

:r to reload


h> :! echo 'hello = print "hello"' > Hello.hs
h> :l Hello
[1 of 1] Compiling Main ( Hello.hs, interpreted )
Ok, modules loaded: Main.
h> hello
"hello"
h> :! echo 'hello = print "HELLO"' > Hello.hs
h> :r
[1 of 1] Compiling Main ( Hello.hs, interpreted )
Ok, modules loaded: Main.
h> hello
"HELLO"

Declarative

  • "Describe the problem, not the solution"
  • Great syntax for this (but not C-like)!
  • Functional code is (often) easier to understand and test
  • Pure, no side-effects?!

-- MergeSort1.hs
module MergeSort1 (mergeSort) where

-- | Bottom-up merge sort.
mergeSort :: Ord a => [a] -> [a]
mergeSort [] = []
mergeSort xs = mergeAll [[x] | x <- xs]

mergeAll :: Ord a => [[a]] -> [a]
mergeAll [xs] = xs
mergeAll xss  = mergeAll (mergePairs xss)

mergePairs :: Ord a => [[a]] -> [[a]]
mergePairs (a:b:xs) =
  merge a b : mergePairs xs
mergePairs xs = xs

merge :: Ord a => [a] -> [a] -> [a]
merge as@(a:as') bs@(b:bs')
 | a > b     = b : merge as bs'
 | otherwise = a : merge as' bs
merge [] bs = bs
merge as [] = as

-- MergeSort2.hs
module MergeSort2 (mergeSort) where

-- | Bottom-up merge sort.
mergeSort :: Ord a => [a] -> [a]
mergeSort = mergeAll . map (:[])
  where
    mergeAll []   = []
    mergeAll [xs] = xs
    mergeAll xss  = mergeAll (mergePairs xss)

    mergePairs (a:b:xs) =
      merge a b : mergePairs xs
    mergePairs xs = xs

    merge as@(a:as') bs@(b:bs')
      | a > b     = b : merge as bs'
      | otherwise = a : merge as' bs
    merge [] bs = bs
    merge as [] = as
# merge_sort.py
def merge_sort(lst):
    if not lst:
        return []
    lists = [[x] for x in lst]
    while len(lists) > 1:
        lists = merge_lists(lists)
    return lists[0]

def merge_lists(lists):
    result = []
    for i in range(0, len(lists) // 2):
        result.append(merge2(lists[i*2], lists[i*2 + 1]))
    if len(lists) % 2:
        result.append(lists[-1])
    return result

def merge2(xs, ys):
    i = 0
    j = 0
    result = []
    while i < len(xs) and j < len(ys):
        x = xs[i]
        y = ys[j]
        if x > y:
            result.append(y)
            j += 1
        else:
            result.append(x)
            i += 1
    result.extend(xs[i:])
    result.extend(ys[j:])
    return result

-- WordCount1.hs

main :: IO ()
main = do
  input <- getContents
  let wordCount = length (words input)
  print wordCount

-- WordCount2.hs

main :: IO ()
main =
  getContents >>= \input ->
    let wordCount = length (words input)
    in print wordCount

-- WordCount3.hs

main :: IO ()
main = getContents >>= print . length . words

what.the >>=?

  • do is just syntax sugar for the >>= (bind) operator.
  • IO is still purely functional, we are just building a graph of actions, not executing them in-place!
  • Starting from main, the Haskell runtime will interpret these actions
  • It works much like continuation passing style, with a state variable for the current world state (behind the scenes)
  • There are ways to cheat and write code that is not pure, but you will have to go out of your way to do it

Common combinators


-- Function composition
(.) :: (b -> c) -> (a -> b) -> a -> c
f . g = \x -> f (g x)

-- Function application (with a lower precedence)
($) :: (a -> b) -> a -> b
f $ x =  f x

Pure

  • Haskell's purity implies referential transparency
  • This means that function invocation can be freely replaced with its return value without changing semantics
  • Fantastic for optimizations
  • Also enables equational reasoning, which makes it easier to prove code correct
GHC compilation phasesParseRenameTypecheckDesugarCoreOptimizeCode genLLVM

Optimizations

  • Common sub-expression elimination
  • Inlining (cross-module too!)
  • Specialize
  • Float out
  • Float inwards
  • Demand analysis
  • Worker/Wrapper binds
  • Liberate case
  • Call-pattern specialization (SpecConstr)

GHC RULES!

  • Term rewriting engine
  • RULES pragma allows library defined optimizations
  • Used to great effect for short cut fusion
  • Example: map f (map g xs) = map (f . g) xs
  • Prevent building of intermediate data structures
  • Commonly used for lists, Text, ByteString, etc.
  • Great incentive to write high-level code!
  • ANY LIBRARY CAN USE THIS!

{-# RULES
"ByteString specialise break (x==)" forall x.
    break ((==) x) = breakByte x
"ByteString specialise break (==x)" forall x.
    break (==x) = breakByte x
  #-}

Lazy

  • Call by need (outside in), not call by value (inside out)
  • Non-strict evaluation separates equation from execution
  • No need for special forms for control flow, no value restriction
  • Enables infinite or cyclic data structures
  • Can skip unused computation (better minimum bounds)
lazy

Call by need

  • Expressions are translated into a graph (not a tree!)
  • Evaluated with STG (Spineless Tagless G-Machine)
  • Pattern matching forces evaluation

-- [1..] is an infinite list, [1, 2, 3, ...]
print (head (map (*2) [1..]))
-- Outside in, print x = putStrLn (show x)
putStrLn (show (head (map (*2) [1..]))
-- head (x:_) = x
-- map f (x:xs) = f x : map f xs
-- desugar [1..] syntax
putStrLn (show (head (map (*2) (enumFrom 1))))
-- enumFrom n = n : enumFrom (succ n)
putStrLn (show (head (map (*2) (1 : enumFrom (succ 1)))))
-- apply map
putStrLn (show (head ((1*2) : map (*2) (enumFrom (succ 1)))))
-- apply head
putStrLn (show (1*2))
-- show pattern matches on its argument
putStrLn (show 2)
-- apply show
putStrLn "2"

if' :: Bool -> a -> a -> a
if' cond a b = case cond of
  True  -> a
  False -> b

(&&) :: Bool -> Bool -> Bool
a && b = case a of
  True  -> b
  False -> False

const :: a -> b -> a
const x = \_ -> x

fib :: [Integer]
fib = 0 : 1 : zipWith (+) fib (tail fib)

cycle :: [a] -> [a]
cycle xs = xs ++ cycle xs

iterate :: (a -> a) -> a -> [a]
iterate f x = x : iterate f (f x)

takeWhile :: (a -> Bool) -> [a] -> [a]
takeWhile _ [] = []
takeWhile p (x:xs)
  | p x       = x : takeWhile p xs
  | otherwise = []

Types

  • Enforce constraints at compile time
  • No NULL
  • Can have parametric polymorphism and/or recursion
  • Built-in types are not special (other than syntax)
  • Typeclasses for ad hoc polymorphism (overloading)

h> let f x = head True

<interactive>:23:16:
    Couldn't match expected type `[a0]' with actual type `Bool'
    In the first argument of `head', namely `True'
    In the expression: head True
    In an equation for `f': f x = head True

h> let f x = heads True

<interactive>:24:11:
    Not in scope: `heads'
    Perhaps you meant one of these:
      `reads' (imported from Prelude), `head' (imported from Prelude)

No Null References

  • Haskell doesn't repeat Hoare's "Billion Dollar Mistake"
  • Instead of NULL, wrap the value in a sum type such as Maybe or Either
  • Bottom ⊥ can be expressed in any type, as it's always possible to express a computation that does not terminate

data Maybe a = Just a
             | Nothing

data Either a b = Left a
                | Right b

parseBit :: Char -> Maybe Int
parseBit '0' = Just 0
parseBit '1' = Just 1
parseBit _ = Nothing

h> let x = x in x
-- Infinite recursion, not a fun case to deal with!

h> case False of True -> ()
*** Exception: <interactive>:29:1-24: Non-exhaustive patterns in case

h> head []
*** Exception: Prelude.head: empty list

h> error "this throws an exception"
*** Exception: this throws an exception

h> undefined
*** Exception: Prelude.undefined

-- Polymorphic and recursive
data List a = Cons a (List a)
            | Nil
            deriving (Show)

data Tree a = Leaf a
            | Branch (Tree a) (Tree a)
            deriving (Show)

listMap :: (a -> b) -> List a -> List b
listMap _ Nil         = Nil
listMap f (Cons x xs) = Cons (f x) (listMap f xs)

treeToList :: Tree a -> List a
treeToList root = go root Nil
  where
    -- Note that `go` returns a function!
    go (Leaf x)     = Cons x
    go (Branch l r) = go l . go r

-- (), pronounced "unit"
unit :: ()
unit = ()

-- Char
someChar :: Char
someChar = 'x'

-- Instances of Num typeclass
someDouble :: Double
someDouble = 1

-- Instances of Fractional typeclass
someRatio :: Rational
someRatio = 1.2345

-- [a], type can be written prefix as `[] a`
someList, someOtherList :: [Int]
someList = [1, 2, 3]
someOtherList = (:) 4 (5 : (:) 6 [])

-- (a, b), can be written prefix as `(,) a b`
someTuple, someOtherTuple :: (Int, Char)
someTuple = (10, '4')
someOtherTuple = (,) 4 '2'

-- [Char], also known as String
-- (also see the OverloadedStrings extension)
someString :: String
someString = "foo"

Typeclasses

  • Used for many of the Prelude operators and numeric literals
  • Ad hoc polymorphism (overloading)
  • Many built-in typeclasses can be automatically derived (Eq, Ord, Enum, Bounded, Show, and Read)!

module List where

data List a = Cons a (List a)
            | Nil

instance (Eq a) => Eq (List a) where
  (Cons a as) == (Cons b bs) = a == b && as == bs
  Nil         == Nil         = True
  _           == _           = False

instance Functor List where
  fmap _ Nil         = Nil
  fmap f (Cons x xs) = Cons (f x) (fmap f xs)

{-# LANGUAGE DeriveFunctor #-}

module List where

data List a = Cons a (List a)
            | Nil
            deriving (Eq, Functor)

import Data.List (sort)

newtype Down a = Down { unDown :: a }
                 deriving (Eq)

instance (Ord a) => Ord (Down a) where
  compare (Down a) (Down b) = case compare a b of
    LT -> GT
    EQ -> EQ
    GT -> LT

reverseSort :: Ord a => [a] -> [a]
reverseSort = map unDown . sort . map Down

Abstractions

Monoid
Has an identity and an associative operation
Functor
Anything that can be mapped over (preserving structure)
Applicative
Functor, but can apply function from inside
Monad
Applicative, but can return any structure

Monoid

class Monoid a where
  mempty :: a
  mappend :: a -> a -> a

instance Monoid [a] where
  mempty = []
  mappend = (++)

infixr 6 <>
(<>) :: (Monoid a) => a -> a -> a
(<>) = mappend

Functor

class Functor f where
  fmap :: (a -> b) -> f a -> f b

instance Functor [] where
  fmap = map

instance Functor Maybe where
  fmap f (Just x) = Just (f x)
  fmap _ Nothing  = Nothing

infixl 4 <$>
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap

Applicative

class (Functor f) => Applicative f where
  pure :: a -> f a
  infixl 4 <*>
  (<*>) :: f (a -> b) -> f a -> f b

instance Applicative [] where
  pure x = [x]
  fs <*> xs = concatMap (\f -> map f xs) fs

instance Applicative Maybe where
  pure = Just
  Just f <*> Just x = Just (f x)
  _      <*> _      = Nothing

Monad

class Monad m where
  return :: a -> m a
  (>>=) :: m a -> (a -> m b) -> m b
  (>>)  :: m a -> m b -> m b
  ma >> mb = ma >>= \_ -> mb

instance Monad [] where
  return = pure
  m >>= f = concatMap f m

instance Monad Maybe where
  return = pure
  Just x  >>= f = f x
  Nothing >>= _ = Nothing

Parser Combinators

{-# LANGUAGE OverloadedStrings #-}
module SJSON where
import Prelude hiding (concat)
import Data.Text (Text, concat)
import Data.Attoparsec.Text
import Control.Applicative

data JSON = JArray [JSON]
          | JObject [(Text, JSON)]
          | JText Text
          deriving (Show)

pJSON :: Parser JSON
pJSON = choice [ pText, pObject, pArray ]
  where
    pString = concat <$> "\"" .*> many pStringChunk <*. "\""
    pStringChunk = choice [ "\\\"" .*> pure "\""
                          , takeWhile1 (not . (`elem` "\\\""))
                          , "\\" ]
    pText = JText <$> pString
    pPair = (,) <$> pString <*. ":" <*> pJSON
    pObject = JObject <$> "{" .*> (pPair `sepBy` ",") <*. "}"
    pArray = JArray <$> "[" .*> (pJSON `sepBy` ",") <*. "]"

Foreign Function Interface


{-# LANGUAGE ForeignFunctionInterface #-}

import Foreign.C.Types
import Control.Monad

foreign import ccall unsafe "stdlib.h rand"
     c_rand :: IO CUInt

main :: IO ()
main = replicateM_ 20 (c_rand >>= print)

Parallel Programming

-- FlipImage.hs
import System.Environment
import Data.Word
import Data.Array.Repa hiding ((++))
import Data.Array.Repa.IO.DevIL
import Data.Array.Repa.Repr.ForeignPtr

main :: IO () 
main = do
  [f] <- getArgs
  (RGB v) <- runIL $ readImage f
  rotated <- (computeP $ rot180 v) :: IO (Array F DIM3 Word8)
  runIL $ writeImage ("flip-"++f) (RGB rotated)

rot180 :: (Source r e) => Array r DIM3 e -> Array D DIM3 e
rot180 g = backpermute e flop g
  where
    e@(Z :. x :. y :. _) = extent g
    flop (Z :. i         :. j         :. k) =
         (Z :. x - i - 1 :. y - j - 1 :. k)

Concurrency

RAM footprint per unit of concurrency (approx)

1.3KB
Haskell ThreadId + MVar (GHC 7.6.3, 64-bit)
2.6 KB
Erlang process (64-bit)
8.0 KB
Go goroutine
64.0 KB
Java thread stack (minimum)
64.0 KB
C pthread stack (minimum)

1 MB
Java thread stack (default)
8 MB
C pthread stack (default, 64-bit Mac OS X)

import Control.Concurrent
import Network.HTTP

getHTTP :: String -> IO String
getHTTP url = simpleHTTP (getRequest url) >>= getResponseBody

urls :: [String]
urls = map ("http://ifconfig.me/"++) ["ip", "host"]

startRequest :: String -> IO (MVar ())
startRequest url = do
  v <- newEmptyMVar
  forkIO (getHTTP url >>= putStr >> putMVar v ())
  return v

main :: IO ()
main = do
  mvars <- mapM startRequest urls
  mapM_ takeMVar mvars

Why not Haskell?

  • Lots of new terminology
  • Mutable state takes more effort
  • Laziness changes how you need to reason about code
  • Once you get used to it, these aren't problematic

A monad is just a monoid in the category of endofunctors, what's the problem?

Terminology from category theory can be intimidating (at first)!

return probably doesn't mean what you think it means.

function main() {
  var foo = {bar: 1, baz: 20};
  while (foo.baz > foo.bar) {
    foo.bar += 1;
  }
  console.log(foo);
}
import Control.Concurrent
data Foo = Foo {bar :: Int, baz :: Int}
         deriving (Show)

main :: IO ()
main = do
  fooVar <- newMVar (Foo { bar = 1, baz = 20 })
  let whileLoop = do
      foo <- takeMVar fooVar
      if baz foo > bar foo
      then do
        putMVar fooVar (foo { bar = 1 + bar foo })
        whileLoop
      else
        putMVar fooVar foo
  whileLoop
  withMVar fooVar print

sum :: Num a => [a] -> a
sum []     = 0
sum (x:xs) = x + sum xs
sum :: Num [a] => [a] -> a
sum = go 0
  where
    go acc (x:xs) = go (acc + x) (go xs)
    go acc []     = acc
sum :: Num [a] => [a] -> a
sum = go 0
  where
    go acc _
      | seq acc False = undefined
    go acc (x:xs)     = go (acc + x) (go xs)
    go acc []         = acc
{-# LANGUAGE BangPatterns #-}

sum :: Num [a] => [a] -> a
sum = go 0
  where
    go !acc (x:xs) = go (acc + x) (go xs)
    go  acc []     = acc

Notable Libraries

Web Frameworks

Snap
HTTP + Templates. Extensible with "Snaplets"
Yesod
Full stack, uses Template Haskell
Happstack
Full stack, does not rely on Template Haskell (happstack-lite)
scotty
Like Ruby Sinatra, great for simple REST apps

Publishing and docs

Haddock
Standard library documentation tool for Haskell projects
diagrams
DSL for vector graphics
hakyll
Static site generator
Pandoc
Markup format swiss-army knife (Markdown, LaTeX, EPUB, …)

Parser Combinators

Parsec
Industrial strength, monadic parser combinator library for Haskell
attoparsec
Like Parsec, but makes a few trade-offs for performance

Dev Tools

HLint
Suggests improvements for your code
ghc-mod, hdevtools
Editor integration
Hoogle, Hayoo
Search for Haskell functions by name or by type!
Djinn
Automatically generate code given a type!
tidal
DSL for live coding music patterns ("algorave")

Parallel / Distributed

repa
High performance, regular, multi-dimensional arrays (with multi-core!)
accelerate
Like repa, but can utilize CUDA to run on GPUs
Cloud Haskell
Erlang-like concurrency and distribution for Haskell

Testing & Profiling

QuickCheck
Property based testing
HUnit
Standard unit testing framework
hpc
Haskell Program Coverage
ThreadScope
Visualize multi-core utilization
criterion
Gold standard for performance benchmarking
EKG
Embeds a web-server for live monitoring of metrics

Learn More

Books
Learn You a Haskell for Great Good
Parallel and Concurrent Programming in Haskell
Real World Haskell
Lectures
Functional Systems in Haskell - CS240h Autumn 2011, Stanford
Introduction to Haskell - CS1501 Spring 2013, UVA
Introduction to Haskell - CIS 194 Spring 2013, UPenn
Haskell Track - CS 11 Fall 2011, Caltech
Practice
exercism.io, Talentbuddy, HackerRank
H-99, Project Euler

Thanks!

Course

Intro to Haskell

Slides

http://bob.ippoli.to/why-haskell-2013/

Source

github.com/etrepum/why-haskell-2013

Email

bob@redivi.com

Twitter

@etrepum

PREVIEW: Intro to Haskell

By Bob Ippolito