Lesson 3 / 25

Expressions, Types and Functions

Write functions with type signatures, guards, where and let, and use operators.

Everything is an expression

A Haskell program is built from expressions and definitions. Basic types include Int (fixed-size), Integer (arbitrary precision), Double, Bool, Char and String (a list of Char; real projects use Text). Write a type signature above each top-level function: withGst :: Int -> Int. A function of several arguments has type Int -> Int -> Int: functions are curried, taking one argument at a time, so partial application (add 5) produces a new function. Application binds tighter than any operator: f x + 1 means (f x) + 1. if then else is an expression and always has an else. Guards (| condition = result) choose between cases and otherwise catches the rest. where defines helper values after a function, and let ... in defines them before an expression. Operators are functions: (+) 2 3, and any function can be used infix with backticks, as in 7 `div` 2. Sections partially apply operators: (* 2), (subtract 1). Numeric literals are polymorphic, and conversions are explicit: fromIntegral converts integers to other numeric types. Integer division uses div and mod; / is for fractional types.

Functions with guards and where

Prices in paise to avoid floating-point rounding.

-- Integer paise avoid floating-point surprises with money
withGstPaise :: Int -> Int
withGstPaise paise = paise + (paise * 18 + 50) `div` 100   -- rounds to the nearest paisa

shippingFee :: Int -> Bool -> Int
shippingFee totalPaise express
  | totalPaise >= 100000 = 0
  | express              = 9900
  | otherwise            = 4900

bmiCategory :: Double -> Double -> String
bmiCategory weightKg heightM
  | bmi < 18.5 = "underweight"
  | bmi < 25   = "normal"
  | otherwise  = "overweight"
  where
    bmi = weightKg / (heightM * heightM)

average :: [Int] -> Double
average [] = 0
average xs = fromIntegral (sum xs) / fromIntegral (length xs)

addTax :: Int -> Int -> Int
addTax rate amount = amount + amount * rate `div` 100

main :: IO ()
main = do
  print (withGstPaise 10000)          -- 11800
  print (shippingFee 20000 True)      -- 9900
  putStrLn (bmiCategory 68 1.75)      -- normal
  print (7 `div` 2, 7 `mod` 2)        -- (3,1)
  let gst18 = addTax 18               -- partial application
  print (map gst18 [100, 250])        -- [118,295]

Write the type first

Writing the type signature before the body clarifies what a function should do, gives better error messages, and lets you use typed holes (_) so GHC tells you what type is needed at that spot.

Quick check: What is the type of `addTax 18` if `addTax :: Int -> Int -> Int`?

  • Int
  • Int -> Int -> Int
  • Int -> Int
  • It is a type error
Answer

Int -> Int — Curried functions return a new function when partially applied.