Saturday, March 23, 2013

HASKELL BEGINNER PROGRAM SKELETON: "MODULE MAIN"

HASKELL BEGINNER PROGRAM SKELETON: "MODULE MAIN"

REVISED: Saturday, February 10, 2024




Haskell Beginner Program Skeleton: A "Module Main" Tutorial.

I. MODULE MAIN EXAMPLE

Most people learn a new computer programming language faster by programming in that language. This tutorial shows a Haskell program skeleton a beginner can use to start programming in Haskell. First you are shown the skeleton of a program. Then you are shown how to flesh out the bones of the skeleton to write your own programs. This gives you a feedback loop which will allow you to run the code you write to see if the program output is what you intended it to be. By changing and rerunning programs you learn Haskell.

A. SKELETON

-- MODULE DECLARATION
module Main where

-- IMPORTS

-- MAIN PROGRAM
main :: IO ()
main = do


-- FUNCTIONS 

B. FLESH ON BONES

1. SAVE

Use your editor to save the following Main.hs file:

module Main where

main :: IO ()
main = do  
       putStrLn "What is your favorite pet? "  
       pet <- getLine  
       putStrLn $"Amazing! My favorite pet is also a " ++ pet ++ "!" 

2. LOAD

As shown below, load the above Main.hs file into GHCi:

Prelude>  :load Main.hs
[1 of 1] Compiling Main       ( Main.hs, interpreted )
Ok, modules loaded: Main.
Prelude>

Now that we have loaded Main, we can run functions in GHCi that were defined in Main.

3. RUN

As shown below, run the module Main, function main, in GHCi:

Prelude>  main
What is your favorite pet? 
Dog
Amazing! My favorite pet is also a Dog!
Prelude>

4. COMPILE 

As shown below, compile Main.hs in GHC:

Prelude>  :! ghc --make "*Main"
[1 of 1] Compiling Main             ( Main.hs, Main.o )
Linking Main.exe ...
Prelude>  

The compile is shown so you know it compiles without error.

C. COMMENTS

Use the program shown above as an example. Rewrite the example and make it your own program.

Repeat 1. thru 3. above until your new program works the way you want it to work. Each time you make changes in your editor to your program, make sure you save the file and load it in GHCi.

The Haskell keywords used in this tutorial are discussed below:

1. module

Large programming projects are divided in parts, containing pieces of the program that belong together. These parts are called modules.

Module names are alphanumeric and must begin with an uppercase letter. The module name, in most cases, should match the filename.

You cannot have multiple modules in the same file.

2. main

main :: IO () is the main function signature.

Function main is the "entry point of a Haskell program". In order for a program to be an executable, it must have the module named Main and must contain a function called main. The type of main is that of its final expression.

3. do

do is syntactic sugar for the function bind >>=. The signature for do is:

Prelude>  :type (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
Prelude>

The function bind >>= combines a monad instance m a with a computation that produces another monad instance m b from a's to produce a new monad instance m b.  

A monad is a type constructor, a function called return, and a combinator function called bind or >>=. These three elements work together to encapsulate a strategy for combining computations to produce more complex computations.

Using a "container analogy", the type constructor m is a "container" that can hold different values. m a is a "container" holding a value of type a. The return function puts a value into a "monad container". The >>= function takes the value from a "monad container" and passes it to a function to produce a "monad container" containing a new value, possibly of a different type. The >>= function is known as "bind" because it binds the value in a "monad container" to the first argument of a function. By adding logic to the binding function, a monad can implement a specific strategy for combining computations in the monad.

You use do syntax to glue together several I/O actions into one.  This way you are guaranteed that I/O actions are executed in order. The value from the last action in a do block is bound to the do block result.

do notation is simply syntactic sugar. There is nothing that can be done using do notation that cannot be done using only the standard monadic operators. But do notation is cleaner and more convenient in some cases, especially when the sequence of monadic computations is long. You should understand both the standard monadic binding notation and do notation and be able to apply each where they are appropriate.

The standard language library, named "Prelude", provides us with lots of functions that return useful primitive IO actions. In order to combine them to produce an even more complex actions, we use a "do":

c = do a <- someAction
       b <- someOtherAction
       print (bar b)
       print (foo a)
       putStrLn "done"

Here we bind "c" to an action with the following "scenario":

evaluate action "someAction" and bind its result to "a"
then, evaluate "someOtherAction" and bind its result to "b"
then, process "b" with function "bar" and print result
then, process "a" with function "foo" and print result
then, print the word "done"

4. getLine

Prelude> :type getLine
getLine :: IO String
Prelude>

getLine is an "IO action" that, when performed, produces a string.

5. putStrLn

Prelude> :type putStrLn
putStrLn :: String -> IO ()
Prelude>

The () is an empty tuple, pronounced “unit”, indicating that there is no return value from putStrLn.

6. <-

Use the <- arrow function when you want to bind results of I/O actions to names.

7. ++

Haskell's name for the “append” function is (++).

Prelude>  "Has" ++ "kell"
"Haskell"
Prelude>  

8. $

In Haskell, function application is left associative. Left associative means operators are evaluated from left to right. For example, addition and subtraction have the same precedence and they are left-associative. In the expression 10-4+2, the subtraction is done first because it is to the left of the addition, producing a value of 8.

When an argument to a function is a result of another function, you have to use parentheses.

For example:

a b (c d)

means the function a acting on two arguments, b and (c d), which itself is an application of function c to d.

The parentheses can be removed by using the $ operator.

a b $ c d 

The $ function is also called "function application". Function application with $ is right-associative. Using $ we do not have to write as many parentheses. When a $ is encountered, the expression on its right is applied as the parameter to the function on its left. $ means that function application can be treated just like another function. For example, we can map function application over a list of functions.  

Prelude> :t ($)
($) :: (a -> b) -> a -> b
Prelude>

$ has the lowest precedence of any operator. The apply $ operator is used to leave out the closing parentheses in a Haskell statement. When you see a $ mentally replace it with a left open parentheses,  and mentally place the right closing parentheses at the end of the statement. Neither a $ nor a closing parentheses is coded at the end of the statement.

II. CONCLUSION

In this tutorial, you have received an introduction to the Haskell beginner program skeleton "module main".

III. REFERENCES

Bird, R. (2015). Thinking Functionally with Haskell. Cambridge, England: Cambridge University Press.

Davie, A. (1992). Introduction to Functional Programming Systems Using Haskell. Cambridge, England: Cambridge University Press.

Goerzen, J. & O'Sullivan, B. &  Stewart, D. (2008). Real World Haskell. Sebastopol, CA: O'Reilly Media, Inc.

Hutton, G. (2007). Programming in Haskell. New York: Cambridge University Press.

Lipovača, M. (2011). Learn You a Haskell for Great Good!: A Beginner's Guide. San Francisco, CA: No Starch Press, Inc.

Thompson, S. (2011). The Craft of Functional Programming. Edinburgh Gate, Harlow, England: Pearson Education Limited.