Learn Haskell — 1 Link to heading

So, I am going to attempt to learn Haskell, yet again. I have tried a few times in the past, but every time it was an utter failure. This time, I am going to write articles as my learning notes to force myself keep going. If you are also interested in learning Haskell, join the journey with me!

First, like ever other programming language, we need to start with HelloWorld. What is fun in learning a new programming language if you don’t get to run it? Create hello.hs file as below

-- hello.hs
main = putStrLn "hello, world"

and let’s compile and run!

$ ghc --make hello
[1 of 1] Compiling Main             ( hello.hs, hello.o )
Linking hello ...

$ ./hello
hello, world

Now that we are able to print out, let’s also read from stdin:

-- name.hs
main = do
    putStrLn "hello, what is your name?"
    name <- getLine
    putStrLn ("welcome " ++ name ++ "!")

Again, let’s compile and run

$ ghc --make name
$ echo "hara" | ./name
hello, what is your name?
welcome hara!

So far so good. Let’s spice our program a bit more

-- lower_upper.hs
import Data.Char
main = do
  putStrLn "your first name?"
  first_name <- getLine
  putStrLn "your last name?"
  last_name <- getLine
  let first_name_lower = map toLower first_name
  let last_name_upper = map toUpper last_name
  putStrLn ("hello " ++ first_name_lower ++ " " ++ last_name_upper)

Compile and run!

$ echo -e "tech\nhara" | ./lower_upper
your first name?
your last name?
hello tech HARA

Let’s do something more difficult. Read input line by line and reverse the characters within each word of the line and print.

-- reverse.hs
main = do
    line <- getLine
    if null line
        then return ()
    else do
        putStrLn (reverseWords line)
        main

reverseWords :: String -> String
reverseWords = unwords . map reverse . words

Here is interpretation of the program (with help from Bing Chat!)

  • main is the entry point of the program. It contains a do block which allows for sequencing of actions.
  • line <- getLine reads a line of text from the standard input (usually the keyboard) and binds it to the variable line.
  • if null line then return () else do checks if the line is empty. If it is, the program does nothing and returns (), which is the unit type, signifying no meaningful value (similar to void in other languages). If the line is not empty, it proceeds to the else block.
  • putStrLn (reverseWords line) calls the reverseWords function with line as an argument and prints the result.
  • main at the end of the else block is a recursive call to the main function, causing the program to start over and read another line.

The reverseWords function is defined as follows:

  • reverseWords :: String -> String is the type signature of the function, indicating that it takes a String as input and returns a String as output.
  • reverseWords = unwords . map reverse . words is the function definition. It uses function composition (.) to chain together three functions:
  • words splits the input string into a list of words.
  • map reverse applies the reverse function to each word in the list, reversing them.
  • unwords takes the list of reversed words and concatenates them back into a single string with spaces between words.

Let’s run and test it

$ ./reverse < reverse.hs
-- sh.esrever
niam = od
enil <- eniLteg
fi llun enil
neht nruter )(
esle od
nLrtStup sdroWesrever( )enil
niam

sdroWesrever :: gnirtS -> gnirtS
sdroWesrever = sdrownu . pam esrever . sdrow

I am actually liking Haskell! The code is concise and to the point! Just for comparison, here is equivalent code in Rust

use std::io::{stdin, BufRead, Result};

fn main() -> Result<()> {
    let input = stdin().lock();
    let iter = input.lines().map(|line| line.map(reverse_words));
    for line in iter {
        println!("{}", line?);
    }
    Ok(())
}

fn reverse_words(line: String) -> String {
    line.split_whitespace()
        .map(|word| word.chars().rev().collect::<String>())
        .collect::<Vec<_>>()
        .join(" ")
}

Reference Link to heading

http://learnyouahaskell.com/input-and-output#hello-world