Lecture: Haskell Mains

Dear Computer

Chapter 7: Immutability and I/O

Lecture: Haskell Mains

Dear students:

The way to get better at new things is to actively throw yourself at them. Haskell's IO system and immutability is different, so we'll spend this lecture throwing ourselves at a series of small problems that require input and output.

power2

Write a program that asks the user for a integer \(n\) and prints \(2^n\).

Here's one possible solution:

Haskell
main = do
  text <- getLine
  let n = read text :: Int
  print $ 2 ^ n

multiplier

Write a program that multiplies two numbers entered by the user.

Here's one possible solution:

Haskell
main = do
  lineX <- getLine
  let x = read lineX :: Int
  lineY <- getLine
  let y = read lineY :: Int
  print $ x * y

span

Write a program that receives a bunch of integers as command-line arguments. It prints the range of the numbers. For example:

> runhaskell main.hs 7 3 80 9
3-80

Here's one possible solution:

Haskell
import System.Environment

stringsToInts :: [String] -> [Int]
stringsToInts strings =
  if null strings then
    []
  else
    i : stringsToInts rest
    where
      first = head strings
      rest = tail strings
      i = read first :: Int

main = do
  args <- getArgs
  let sortedArgs = sort $ stringsToInts args
  let alpha = head sortedArgs
  let omega = last sortedArgs
  putStrLn $ show alpha ++ "-" ++ show omega

In a week's time, we are going to write this much shorter program:

Haskell
main = do
  args <- getArgs
  let sortedArgs = sort $ map read args :: [Int]
  let alpha = head sortedArgs
  let omega = last sortedArgs
  putStrLn $ show alpha ++ "-" ++ show omega

sum

Write a program that prints an equation summing up the command-line arguments. One side shows the addition expression, while the other shows the total.

Here's one possible solution:

Haskell
import System.Environment
import Data.List

stringsToInts :: [String] -> [Int]
stringsToInts ss =
  if null ss then
    []
  else
    (read $ head ss :: Int) : (stringsToInts $ tail ss)

main = do
  args <- getArgs
  putStr $ intercalate " + " args
  putStr " = "
  print $ sum $ stringsToInts args

number

Write a program that echoes the lines of a file, prefixing each line with its number. The file path is passed as a command-line argument.

Here's one possible solution:

Haskell
import System.Environment

printNumberedLines :: Int -> [String] -> IO ()
printNumberedLines i lines =
  if null lines then
    return ()
  else do
    putStr $ show i
    putStr " "
    putStrLn $ head lines
    printNumberedLines (i + 1) $ tail lines

main = do
  args <- getArgs
  let path = head args
  text <- readFile path
  let rows = lines text
  printNumberedLines 0 rows

cat

Write a program like cat that dumps all files passed as command-line arguments to standard out.

Here's one possible solution:

Haskell
import System.Environment

catFiles :: [String] -> IO ()
catFiles files = do
  if null files then
    return ()
  else do
    text <- readFile $ head files
    putStr text
    catFiles $ tail files

main = do
  args <- getArgs
  catFiles args

spell

Write a program that spells out the phrase passed as a command-line argument, one character at a time. Use the say utility, which is builtin on macOS. Linux has espeak. I don't know about Windows.

Here's one possible solution:

Haskell
import System.Environment
import System.Process

spell :: String -> IO ()
spell chars =
  if null chars then
    return ()
  else do
    if head chars == ' ' then
      system $ "say space"
    else do
      system $ "say " ++ [head chars]
    spell $ tail chars

main = do
  args <- getArgs
  let chars = head args
  spell chars

Advent of Code 2015, Day 1

Write a program that solves Advent of Code 2015, Day 1. Accept the input text as a command-line argument.

Here's one possible solution:

Haskell
import System.Environment

deliver :: String -> Int -> Int
deliver directions floor =
  if directions == "" then
    floor
  else
    deliver rest floor'
    where
      first = head directions
      rest = tail directions
      offset = if first == '(' then 1 else -1
      floor' = floor + offset

main = do
  args <- getArgs
  let directions = head args
  print $ deliver directions 0

Note that this follows the fold pattern. We could write a much simpler solution using tools we discuss next week.

TODO

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

Complete the middle quiz as desired.
There's a ready date coming up.
Keep learning Haskell from videos and books.

See you next time.

Sincerely,

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

You've got the wrong man That wasn't me at the wheel Only ex-me speeds
← AcrosticLab: Haskell Mains →