haskell.day
haskell.day :: IO ()
01 / purity
purity :: Side Effects -> Controlled Side Effects
-- pure functions return values
double :: Int -> Int
double x = x * 2
-- effects are types
readName :: IO String
readName = getLine
main :: IO ()
main = do
name <- readName
putStrLn "Hello, " ++ name
In Haskell, a function is a promise. Given the same arguments, it returns the same value -- always. No hidden state, no surprise side effect, no mutation in the dark. Effects do not vanish; they become first-class types.
The IO type is not a sandbox you escape from -- it is the contract that says: this computation touches the world. A pure function can never call an impure one without saying so in its type. The compiler is your auditor.
02 / types
types :: Runtime Errors -> Compile Time Guarantees
-- algebraic data types
data Maybe a = Nothing | Just a
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv a b = Just (a `div` b)
-- the impossible is unrepresentable
data NonEmpty a = a :| [a]
Haskell's type system is not a tax -- it is a conversation. You describe the shape of the data; the compiler tells you what is possible and what is impossible. Bugs become unutterable.
Maybe a says: there might be a value, there might not be. The function that uses it cannot pretend the absence does not exist. The illegal state simply does not type-check.
03 / laziness
laziness :: Eager Evaluation -> On Demand
-- an infinite list, defined precisely
naturals :: [Int]
naturals = [0..]
-- only what is asked is computed
firstTen :: [Int]
firstTen = take 10 naturals
-- fibonacci, the way mathematicians write it
fibs :: [Integer]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Haskell evaluates only what is needed, only when it is needed. An infinite list is a finite definition; a value is the unfolding of that definition under demand.
This is not a performance trick -- it is a different relationship to time. Computation becomes the shape of a question, not the schedule of an answer. fibs defines itself in terms of itself, and Haskell knows what to do.
04 / abstraction
abstraction :: Repetition -> Elegant Composition
-- typeclasses generalise structure
class Functor f where
fmap :: (a -> b) -> f a -> f b
instance Functor Maybe where
fmap _ Nothing = Nothing
fmap g (Just x) = Just (g x)
-- composition reads right to left
describe :: Int -> String
describe = show . (+ 1) . (* 2)
Where other languages give you patterns to memorise, Haskell gives you algebra. Functor, Applicative, Monad -- these are not buzzwords; they are laws a structure obeys.
A function is a value. Composition is multiplication. Programs are equations being simplified. Repetition becomes a sign that an abstraction is waiting to be named.
05 / community
community :: Isolation -> Collaboration
-- Hackage: thirteen thousand packages
import Data.Text (Text)
import qualified Data.Text as T
import Network.HTTP.Simple
fetchTitle :: String -> IO Text
fetchTitle url = do
res <- httpBS =<< parseRequest url
pure . T.strip . extractTitle
$ getResponseBody res
Haskell is small in the way a chisel is small -- and around it has grown a community of craftspeople. GHC, Cabal, Stack, Hackage, Stackage. The tools are sharp because the people who make them use them daily.
The conversation happens on Discourse, in IRC, on weekly meetings. Newcomers are not lectured; they are invited to think more carefully and given the company to do so.
λ live session
A short conversation in GHCi.
Every function tells a truth. Every type keeps a promise.