**On Github** etrepum / haskell-for-erlangers-2014

- Not classically trained in CS
- Erlang user since 2006 (Mochi Media, mochiweb, etc.)
- Haskell user since 2012 (ported exercism.io curriculum)
- Currently teaching web technologies to teenagers with Mission Bit
- Doing a bit of advising/investing in startups

- I learn a lot of from studying new languages
- Types are supposed to help you write better software
- I like QuickCheck and Dialyzer
- Good support for parallelism and concurrency
- Will help me understand more CS papers

RAM footprint per unit of concurrency (approx)

1.3KB- Intimidated by Haskell for years
- Took a class while at Facebook
- Read several books
- Deliberate practice

- 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

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

- http://www.haskell.org/platform
- GHC compiler and GHCi interpreter
- Robust and stable set of vetted packages
- Cabal; easily fetch more packages from Hackage

Constructors and record accessors become values

Values Named bindings Instances of constructors FunctionsControl flow

- Syntax is minimal & familiar
- Haskell's pattern matching is not as clever as Erlang's
- Types are kinda like having Dialyzer for every compile (although Dialyzer is really quite different!)
- Typeclasses are nice, Erlang doesn't have them
- Erlang is probably (much) better for long-running systems

map(F, [H|T]) -> [F(H)|map(F, T)]; map(F, []) when is_function(F, 1) -> [].

map _ [] = [] map f (x:xs) = f x : map f xs

-spec map(Fun, List1) -> List2 when Fun :: fun((A) -> B), List1 :: [A], List2 :: [B], A :: term(), B :: term(). map(F, [H|T]) -> [F(H)|map(F, T)]; map(F, []) when is_function(F, 1) -> [].

map :: (a -> b) -> [a] -> [b] map _ [] = [] map f (x:xs) = f x : map f xs

-spec foldr(Fun, Acc0, List) -> Acc1 when Fun :: fun((Elem :: T, AccIn) -> AccOut), Acc0 :: term(), Acc1 :: term(), AccIn :: term(), AccOut :: term(), List :: [T], T :: term(). foldr(F, Accu, [Hd|Tail]) -> F(Hd, foldr(F, Accu, Tail)); foldr(F, Accu, []) when is_function(F, 2) -> Accu.

foldr :: (a -> b -> b) -> b -> [a] -> b foldr k z = go where go [] = z go (y:ys) = y `k` go ys

%% sum type, 3 possible values -type choice() :: definitely | possibly | no_way.

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

%% product type, 9 possible values (3 * 3) -type choices() :: {choice(), choice()}.

-- product type, 9 possible values (3 * 3) data Choices = Choices Choice Choice -- as a tuple with a type alias -- NOT THE SAME AS ABOVE! :) type Choices = (Choice, Choice)

%% record syntax -record(choices, fst_choice :: choice(), snd_choice :: choice()). %% getters need to be implemented manually -spec fst_choice(#choices{}) -> choice(). fst_choice(#choices{fst_choices=X}) -> X. -spec snd_choice(#choices{}) -> choice(). snd_choice(#choices{snd_choices=X}) -> X.

-- record syntax defines accessors automatically data Choices = Choices { fstChoice :: Choice , sndChoice :: Choice } -- these getters are automatically defined fstChoice :: Choices -> Choice fstChoice (Choices { fstChoice = x }) = x sndChoice :: Choices -> Choice sndChoice (Choices { sndChoice = x }) = x

%% abstract data type for a list -type cons(A) :: nil | {cons, A, cons(A)}.

-- abstract data type for a list data List a = Nil | Cons a (List a)

data Choice = Definitely | Possibly | NoWay data Choices = Choices Choice Choice mkChoices :: Choice -> Choice -> Choices mkChoices a b = Choices a b fstChoice :: Choices -> Choice fstChoice (Choices a _) = a

data Choice = Definitely | Possibly | NoWay data Choices = Choices Choice Choice mkChoices :: Choice -> Choice -> Choices mkChoices a b = Choices a b fstChoice :: Choices -> Choice fstChoice (Choices a _) = a

-- Values can be annotated in-line 2 ^ (1 :: Int) -- Bindings can be annotated success :: a -> Maybe a -- Constructors are values -- (and product constructors are functions) success x = Just x -- Constructors can be pattern matched -- _ is a wildcard case success True of Just True -> () _ -> ()

-spec is_just({just, A} | nothing) -> boolean(). is_just({just, _}) -> true; is_just(nothing) -> false.

isJust :: Maybe a -> Bool isJust (Just _) = True isJust Nothing = False

Erlang's pattern matching allows non-linear patterns.

-spec is_equal(A, A) -> boolean() when A :: term(). is_equal(A, A) -> true; is_equal(_, _) -> false.

Haskell's... does not.

isEqual :: a -> a -> Bool isEqual a b = undefinedThis isn't even possible! Only constructors can be pattern matched. Types have no built-in equality.

%% Symbolic operators can be used %% as functions from the erlang module erlang:'+'(A, B).Erlang doesn't have user-defined infix operators

-- 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

-spec add(integer(), integer()) -> integer(). add(X, Acc) -> X + Acc. -spec sum_fun([integer()]) -> integer(). sum_fun(Xs) -> lists:foldl(fun add/2, 0, Xs). -spec sum_lambda([integer()]) -> integer(). sum_lambda(Xs) -> lists:foldl( fun (X, Acc) -> X + Acc end, 0, Xs).

add :: Integer -> Integer -> Integer add acc x = acc + x sumFun :: [Integer] -> Integer sumFun xs = foldl add 0 xs sumLambda :: [Integer] -> Integer sumLambda xs = foldl (\acc x -> acc + x) 0 xs

- Haskell only has functions of one argument
- a -> b -> c is really a -> (b -> c)
- f a b is really (f a) b
- Let's leverage that…

add :: Integer -> Integer -> Integer add acc x = acc + x sumFun :: [Integer] -> Integer sumFun xs = foldl add 0 xs sumLambda :: [Integer] -> Integer sumLambda xs = foldl (\acc x -> acc + x) 0 xs

add :: Integer -> Integer -> Integer add acc x = acc + x sumFun :: [Integer] -> Integer sumFun = foldl add 0 sumLambda :: [Integer] -> Integer sumLambda = foldl (\acc x -> acc + x) 0

add :: Integer -> Integer -> Integer add acc x = (+) acc x sumFun :: [Integer] -> Integer sumFun = foldl add 0 sumLambda :: [Integer] -> Integer sumLambda = foldl (\acc x -> (+) acc x) 0

add :: Integer -> Integer -> Integer add acc = (+) acc sumFun :: [Integer] -> Integer sumFun = foldl add 0 sumLambda :: [Integer] -> Integer sumLambda = foldl (\acc -> (+) acc) 0

add :: Integer -> Integer -> Integer add = (+) sumFun :: [Integer] -> Integer sumFun = foldl add 0 sumLambda :: [Integer] -> Integer sumLambda = foldl (+) 0

-spec is_negative(number()) -> boolean(). is_negative(X) when X < 0 -> true; is_negative(_) -> false.

isNegative :: (Num a) => a -> Bool isNegative x | x < 0 = True | otherwise = False

isNegative :: (Num a) => a -> Bool isNegative x | x < 0 = True | otherwise = False absoluteValue :: (Num a) => a -> Bool absoluteValue x | isNegative x = -x | otherwise = x

-- (), 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

some_list() -> [1, 2, 3]. some_other_list() -> [4 | [5 | [6 | []]]]. some_tuple() -> {10, $4}. some_string() -> "foo".

-- [a], type can be written prefix as `[] a` someList, someOtherList :: [Int] someList = [1, 2, 3] someOtherList = 4 : 5 : 6 : [] dontWriteThis = (:) 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"

Erlang doesn't have typeclasses.

Elixir has Protocols, which are closer, but they are also not typeclasses.

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

{- class Eq a where (==) :: a -> a -> Bool -} instance Eq Choice where Definitely == Definitely = True Possibly == Possibly = True NoWay == NoWay = True _ == _ = False

data Choice = Definitely | Possibly | NoWay deriving (Eq)

data Choice = Definitely | Possibly | NoWay deriving ( Eq, Ord, Enum, Bounded , Show, Read )

prop_itsthere() -> ?FORALL(I,int(), [I] == queue:to_list( queue:cons(I, queue:new()))).

import Data.Sequence ((|>), empty) import Data.Foldable (toList) prop_itsthere :: Int -> Bool prop_itsthere i = [i] == toList (empty |> i)

$ ghci

λ> import Test.QuickCheck λ> import Data.Foldable λ> import Data.Sequence λ> quickCheck (\i -> [i :: Int] == toList (empty |> i)) +++ OK, passed 100 tests.

-spec main([string()]) -> ok. main(_Args) -> {ok, Secret} = file:read_file("/etc/passwd"), file:write_file("/tmp/passwd", Secret), ok.

main :: IO () main = do secret <- readFile "/etc/passwd" writeFile "/tmp/passwd" secret return ()

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

main :: IO () main = do secret <- readFile "/etc/passwd" writeFile "/tmp/passwd" secret return ()

main :: IO () main = readFile "/etc/passwd" >>= \secret -> do writeFile "/tmp/passwd" secret return ()

main :: IO () main = readFile "/etc/passwd" >>= \secret -> writeFile "/tmp/passwd" secret >> return ()

main :: IO () main = readFile "/etc/passwd" >>= \secret -> writeFile "/tmp/passwd" secret

main :: IO () main = readFile "/etc/passwd" >>= writeFile "/tmp/passwd"

-spec flat_map(fun((A) -> [B]), [A]) -> [B] when A :: term(), B :: term(). flat_map(F, Xs) -> [ Y || X <- Xs, Y <- F(X) ].

flatMap :: (a -> [b]) -> [a] -> [b] flatMap f xs = [ y | x <- xs, y <- f x ]

flatMap :: (a -> [b]) -> [a] -> [b] flatMap f xs = do x <- xs y <- f x return y

flatMap :: (a -> [b]) -> [a] -> [b] flatMap f xs = do x <- xs f x

flatMap :: (a -> [b]) -> [a] -> [b] flatMap f xs = xs >>= \x -> f x

flatMap :: (a -> [b]) -> [a] -> [b] flatMap f xs = xs >>= f

flatMap :: (a -> [b]) -> [a] -> [b] flatMap f xs = flip (>>=) f xs

flatMap :: (a -> [b]) -> [a] -> [b] flatMap = flip (>>=)

flatMap :: (a -> [b]) -> [a] -> [b] flatMap = (=<<)

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

$ 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>

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

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'

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 >>=

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'

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"

-- 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

- 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 evaluate 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

-- 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

- 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

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

- 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 #-}

{-# RULES "ByteString specialise break (x==)" forall x. break ((==) x) = breakByte x "ByteString specialise break (==x)" forall x. break (==x) = breakByte x #-} import Data.ByteString.Char8 (ByteString, break) splitLine :: ByteString -> (ByteString, ByteString) splitLine = break (=='\n')

{-# RULES "ByteString specialise break (x==)" forall x. break ((==) x) = breakByte x "ByteString specialise break (==x)" forall x. break (==x) = breakByte x #-} import Data.ByteString.Char8 (ByteString, break) splitLine :: ByteString -> (ByteString, ByteString) splitLine = breakByte '\n'

- 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)

- 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..]))

-- [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..]))

-- 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))))

-- 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)))))

-- 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 map putStrLn (show (head ((1*2) : …))) -- apply head putStrLn (show (1*2))

-- apply head putStrLn (show (1*2)) -- show pattern matches on its argument putStrLn (show 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 = []

- 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)

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

- 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

class Monoid a where mempty :: a mappend :: a -> a -> a instance Monoid [a] where mempty = [] mappend = (++) infixr 6 <> (<>) :: (Monoid a) => a -> a -> a (<>) = mappend

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

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

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

{-# 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` ",") <*. "]"

{-# 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)

-- 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)

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

- 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.

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

Slides

bob.ippoli.to/haskell-for-erlangers-2014

Source

github.com/etrepum/haskell-for-erlangers-2014

bob@redivi.com

0