Saturday, August 09, 2014

Programming in Haskell Series. I: "Introduction and reading a file example"

This is the first article in what I hope will be a series of articles on Haskell. Haskell is a pure functional programming language, with static typing and lazy evaluation. If you fell at home thinking in abstract/mathematical concepts you are going to love Haskell. It really makes you feel like a computer scientist and not as a coding monkey any more.

  • Pure as in functions don't have side effects, which leads to the much desired referential transparency
  • Static typing means that every single value and expression has a type, and it cannot change during the execution of a program. This allows the compiler to catch a lot of mistakes. Besides, Haskell type system is incredibly expressive and flexible. In Haskell you spend most of your type defining types.
  • Lazy as in Haskell only evaluates the minimum amount of information that it requires to keep moving forward with the execution. For instance it is possible to define a infinite list of integers and then request the first element (head in functional programming parlance). In this case only that first element will be determined and the execution will complete happily.

You may be thinking that a language with no side effects is pretty useless. I can't write to a file or the screen, get output from a user, etc. In fact Haskell provides a mechanism to clearly separate your "unsafe"  (with side effects) code from your pure code. These two cannot be mixed. This mechanism is the IO Monad (oh yeah so early and already mentioning Monads).

Now, let's see a bit of code. Hopefully the comments will make it pretty clear

  -- define a new module   
  module Main where   
  -- main is the entry point for the execution of your program  
  -- The first line declares the type (it is optional in Haskell most of the time) whereas  
  -- the second actually defines what the main function is/does  
  main :: IO ()   
  main = do   
    putStr "File name please?"   
    filename <- getLine   
    putStr filename   
    return()   

The Haskell program must have a Main module and a main function. The type of the main function must be IO (), but I won't go into the details now. What we have here is pretty much all unsafe code. This type of code is also monadic (the do construct is syntactic sugar for monad).

Now lets's see how we can actually read all the lines in the file, and then print them all back to the user:


 module Main where  
 main = do  
    putStr "Tell me something\n"  
    filename <- getLine  
    putStr $ "Attempting to open " ++ filename  
    contents <- readFile filename  
    putStr contents  
    return ()  

This is clearly not very functional. Let's do something different. Lets count the number of lines in the file, in a more functional style.


 module Main where  
 main = do  
    putStr "Tell me something\n"  
    filename <- getLine  
    -- Haskell function application associates to the left. The $ function associates  
    -- to the right and saves us a good number of brackes. We want to print the result  
    -- of the expression on the right handside of the $  
    putStr $ "Attempting to open " ++ filename ++ "\n"  
    contents <- readFile filename   
    putStr $ "There are " ++ nbOfLines contents ++ " lines in the file\n"  
    putStr contents  
    return ()  
 -- Here we are declaring the type of the function nbOfLines  
 -- It takes a String and returns a String  
 nbOfLines :: String -> String  
 -- The dot is function composition as in maths. f.g x = f ( g (x) )   
 nbOfLines = show.listSize.lines   
 -- Takes a list of any type and return an Int  
 listSize :: [a] -> Int  
 listSize [] = 0  
 listSize (x:xs) = 1 + (listSize xs)  

How do we execute this? There are many ways of executing Haskell code, in this occasion we are going to compile to machine code and then execute. Install the Haskell Platform and:

$ ghc FilaName.hs -o myexecutable
$ ./myexecutable

There is a lot to understand here. Hopefully we will be able to make progress towards Haskell proficiency in the next posts in this series.