Lecture: Haskell

Dear Computer

Chapter 6: Expressions

Lecture: Haskell

Dear students:

A new language is upon us. New languages require us to regress to infancy. The world around is filled with expressions that we can't understand, and we are uncomfortable. Today we embrace that discomfort and start babbling in Haskell by solving some programming challenges together.

Inventory

To solve these problems, we'll to make use of some of Haskell's builtin functions. These are the ones from the reading plus a few more that you might find useful:

PairSum

Write a function pairSum that, given a pair of numbers, returns their sum. For example:

Here's one way to write it:

Haskell
pairSum :: Num a => (a, a) -> a
pairSum pair = fst pair + snd pair

Maybe you prefer local variables?

Haskell
pairSum :: Num a => (a, a) -> a
pairSum pair = x + y
  where x = fst pair
        y = snd pair

Majority

Write a function majority that, given a population as an Int, returns the number of people that are needed to count as a majority in that people. For example:

Here's one solution:

Haskell
majority :: Int -> Int
majority n = div n 2 + 1

Recall that Haskell operators and functions are the same thing. Functions are prefix operators, but they can be moved to infix:

Haskell
majority :: Int -> Int
majority n = n `div` 2 + 1

Neck

Write a function neck that, given a list, returns its second element. For example:

Here's one solution:

Haskell
neck :: [a] -> a
neck xs = head (tail xs)

Parentheses are annoying to balance and can make code hard to read. Haskell provides an alternative in $. It yields the value of everything to its right, allowing expressions of the form f (g (x)) to be rewritten as f $ g $ x.

Haskell
neck :: [a] -> a
neck xs = head $ tail xs

Next

Write a function next that accepts two numbers in an arithmetic sequence and returns the next number. For example:

Here's one solution:

Haskell
next :: Num a => a -> a -> a
next a b = b + delta
  where delta = b - a

Bigger First

Write a function biggerFirst that, given a pair of lists, returns the one that has a bigger first element. For example:

Here's one solution:

Haskell
biggerFirst :: Ord a => ([a], [a]) -> [a]
biggerFirst pair = if a0 >= b0 then a else b
  where
    a = fst pair
    b = snd pair
    a0 = head a
    b0 = head b

Slice

Write a function slice that, given a list, a starting index, and an ending index, returns the sublist between the given indices. For example:

Here's one solution:

Haskell
slice :: Int -> Int -> [a] -> [a]
slice from to xs = take n afterFroms
  where
    afterFroms = drop from xs
    n = to - from + 1

Abbreviate

Write a function abbreviate that, given a string and an integer length, it returns the string intact if its short or abbreviated if its long. Shortness is determine by the length parameter. For example:

Here's one solution:

Haskell
abbreviate :: String -> Int -> String
abbreviate text n =
  if length text <= n
    text
  else
    [head text] ++ "..." ++ [last text]

Quadrant

Write a function quadrant that accepts a Cartesian coordinate pair of numbers. It returns "I", "II", "III", or "IV" to indicate the pairs quadrant. If the pair doesn't lie in a quadrant, it returns "?". For example:

Here's one solution:

Haskell
quadrant :: (Double, Double) -> String
quadrant xy
  | x > 0 && y > 0 = "I"
  | x < 0 && y > 0 = "II"
  | x < 0 && y < 0 = "III"
  | x > 0 && y < 0 = "IV"
  | otherwise = "?"
  where
    x = fst xy
    y = snd xy

sumHeadNeck

Write a function sumHeadNeck that accepts a list of numbers that has at least two elements. It returns a new list in which the head and the neck have been replaced by their sum.

Here's one solution:

Haskell
sumHeadNeck :: Num a => [a] -> [a]
sumHeadNeck xs = (x0 + x1) : rest
  where
    x0 = head xs
    x1 = neck xs
    rest = drop 2 xs

Main

In a sense, Haskell is two languages: one for its pure functions and one for the messier stuff like printing and running sequences of statements. Writing functions is the clean and beautiful part of Haskell, but sometimes we do need a main routine to set the whole process in motion. Let's take a peak at some very simple mains. The reading will go into more detail about how they work.

Suppose we want a program that prompts the user for the names of two people and then prints a message confessing their love. We'd write it like this:

Haskell
main = do
  first <- getLine
  second <- getLine
  let confession = first ++ "❤️" ++ second
  putStrLn confession

There are two “assignment” operators here. The <- is for assigning messy data gained from impure I/O calls. The let and = are for assigning pure values.

Suppose we want a program that gives us a list of integers between two values. We'd write it like this:

Haskell
main = do
  startString <- getLine
  endString <- getLine
  let start = read startString :: Int
  let end = read endString :: Int
  let xs = [start..end]
  print xs

The read function is the opposite of show. It's like parseInt or parseDouble, but it doesn't have a type baked in. It's polymorphic. So we have to steer the type with the :: operator. We can read it as “as”.

TODO

Here's your list of things to do before we meet next:

Exam scores will go in Canvas soon, but you already know them. Canvas will not ever show you your total grade because I turn that off. Its calculation is misleading because it doesn't handle missing scores well and doesn't take the higher of your quiz scores. Calculating it yourself is well within your power. Use a spreadsheet.
The next chapter—on immutability and I/O—is up. There's also the chapter on expressions that I recommended you read for today. The quizzes cover both of these chapters.
Start learning Haskell. If you like profanity, check out Learn You a Haskell for Great Good! Share useful videos or materials on Discord. Note that some Haskell material can get quite deep. For this class, we only care about its interesting type system, its mechanisms for building functions, and its means of operating under immutability. We will not explore functors, monads, and other advanced abstractions.

See you next time.

Sincerely,

P.S. It's time for a haiku!

Name babies wisely Art will paint and Grace will dance Ralph will disown you
← Evaluation Order