Monday, April 8, 2013

HASKELL MODULES NO DATA HIDING

HASKELL MODULES NO DATA HIDING

REVISED: Monday, February 12, 2024




Haskell Modules No Data Hiding.

I. HASKELL DATA MODULES

You are reading "Haskell Modules No Data Hiding",  the first half of the "Haskell Data Modules" tutorial.

"Haskell Modules Data Hiding"  is the second half.

The two parts of this tutorial are a very brief outline of "Haskell Programming Tutorial 6 - Modules" by Sankha Mukherjee.

A. SKELETON

We start off with the following file containing both the main function and the vector functions:

main = do
       print $ (2,4,5) · (0,1,0)
       print $ (2,4,5) × (0,1,0)

(·) :: (Float, Float, Float) -> (Float, Float, Float) -> Float
(·) (x1, x2, x3)(y1, y2, y3) =  x1*y1+x2*y2+x3*y3

(×) :: (Float, Float, Float) -> (Float, Float, Float) -> (Float, Float, Float)

(×) (x1, x2, x3)(y1, y2, y3) =  (x2*y3-x3*y2, x3*y1-x1*y3, x1*y2-x2*y1)

What happens if we need these vector functions in hundreds of other programs? One solution would be to copy past them into each of the other programs. 

However, what happens if we want to change a function; for example, changing from a three element tuple to a two element tuple? Changes might have to be made to hundreds of files that were copy pasted.

A good programming maintenance technique is to use modules.

One module for each group of related functions.

One module containing the main function.

B. FLESH ON BONES

1. SAVE

Save has two steps. "Save Step1" saves the module containing the vector functions. "Save Step 2" saves the main function.

SAVE STEP 1

Use your editor to save  MyVectorsF.hs the vector dot product, and vector cross product, function signatures and definitions. Notice both of these functions are tied to a particular data type; i.e., Float. Also notice each vector function assumes a "three element tuple". What do you do when you need the dot product or cross product of a vector which has less tuple elements or more tuple elements? That question will be answered in "Haskell Data Hiding".

module MyVectorsF where

(·) :: (Float, Float, Float) -> (Float, Float, Float) -> Float

(·) (x1, x2, x3)(y1, y2, y3) =  x1*y1+x2*y2+x3*y3

(×) :: (Float, Float, Float) -> (Float, Float, Float) -> (Float, Float, Float)

(×) (x1, x2, x3)(y1, y2, y3) =  (x2*y3-x3*y2, x3*y1-x1*y3, x1*y2-x2*y1)

SAVE STEP 2

Use your editor to save MyVetorsM.hs the main function. Notice by importing MyVectorsF you do not have to copy paste the vector functions into the main function.

import MyVectorsF

main = do
    print $ (2,4,5) · (0,1,0)
    print $ (2,4,5) × (0,1,0)

2. LOAD

Load has two steps. "Load Step 1" loads the vector functions. "Load Step 2" loads the main function.

LOAD STEP 1

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

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

LOAD STEP 2

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

Prelude>  :load MyVectorsM
[1 of 2] Compiling MyVectorsF       ( MyVectorsF.hs, interpreted )
[2 of 2] Compiling Main             ( MyVectorsM.hs, interpreted )
Ok, modules loaded: Main, MyVectorsF.
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 main function, in GHCi:

Prelude>  main
4.0
(-5.0,0.0,2.0)
Prelude>  

4. COMPILE 

As shown below, compile MyVectorsM.hs in GHC:

Prelude>  :! ghc --make "*MyVectorsM"
[1 of 2] Compiling MyVectorsF       ( MyVectorsF.hs, MyVectorsF.o )
[2 of 2] Compiling Main             ( MyVectorsM.hs, MyVectorsM.o )
Linking MyVectorsM.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.

II. CONCLUSION

You have just received an introduced to "Haskell Modules No Data Hiding", the first half of the "Haskell Data Modules" tutorial.   "Haskell Modules Data Hiding" is the second half of "Haskell Data Modules".

III. REFERENCE

Haskell Programming by Sankha Mukherjee

Haskell Tutorial 6 - Modules



Sunday, April 7, 2013

HASKELL TOWERS OF HANOI

HASKELL TOWERS OF HANOI

REVISED: Wednesday, January 24, 2024




Haskell Towers of Hanoi.

I. HASKELL TOWERS OF HANOI

The Towers of Hanoi is a game or puzzle. It consists of three vertical anchored rods of equal length and diameter, spaced an equal distance apart on a playing board. The puzzle includes a number of disks of different diameters and equal thickness with a hole in their center slightly larger than the diameter of the rods. The disks can slide onto any of the three rods. The puzzle starts with all the disks in a neat conical stack, the smallest disk at the top, in ascending order of size on one rod, the rod to the far left facing the player. The far right vertical rod can be used as a temporary resting place for the disks as they are moved back and forth between the three rods. The objective of the puzzle is to eventually move the entire stack of disks from the rod on the far left, to the rod in the middle, obeying the following rules:

A. Only one disk may be moved at a time.

B. Each move consists of taking the upper disk from one of the rods and sliding it onto another rod, on top of the other disks that may already be present on that rod.

C. No disk may be placed on top of a smaller disk.

A. SKELETON

main :: IO ()
main = do  

B. FLESH ON BONES

1. SAVE

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

main :: IO ()
main = do
        putStrLn "Given three stationary rods: r1, r2, and r3; enter number of disks you want to move: "
        a <- readLn
        let d = hanoi a "r1" "r2" "r3"
        putStrLn "Towers of Hanoi Disk Movement Solution: "
        putStr d

-- Towers of Hanoi
hanoi :: Integer -> String -> String -> String -> String
hanoi 1 r1 r2 r3 = "From " ++ r1 ++ " to " ++ r2 ++ "\n"
hanoi n r1 r2 r3 =(hanoi (n-1) r1 r3 r2) ++ (hanoi 1 r1 r2 r3) ++ (hanoi (n-1) r3 r2 r1)

2. LOAD

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

Prelude>  :load TowersOfHanoi
[1 of 1] Compiling Main             ( TowersOfHanoi.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 function main, in GHCi:

Prelude>  main
Given three stationary rods: r1, r2, and r3; enter number of disks you want to move: 
3
Towers of Hanoi Disk Movement Solution: 
From r1 to r2
From r1 to r3
From r2 to r3
From r1 to r2
From r3 to r1
From r3 to r2
From r1 to r2
Prelude>  

4. COMPILE 

As shown below, compile TowersOfHanoi.hs in GHC:

Prelude>  :! ghc --make "*TowersOfHanoi"
[1 of 1] Compiling Main             ( TowersOfHanoi.hs, TowersOfHanoi.o )
Linking TowersOfHanoi.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.

II. REFERENCE

Haskell Programming by Sankha Mukherjee

Haskell Tutorial 3 - Recursion

In this tutorial, you have been introduced to a Haskell recursive solution for the Towers of Hanoi.


Wednesday, April 3, 2013

HASKELL BEGINNER PROGRAM SKELETON: "READ AND SHOW" TUTORIAL

HASKELL BEGINNER PROGRAM SKELETON: "READ AND SHOW" TUTORIAL

REVISED: Monday, October 21, 2024




Haskell Beginner Program Skeleton: "Read and Show" Tutorial.

I. EXAMPLE OF READ AND SHOW

A. SKELETON

-- MODULE DECLARATION

-- IMPORTS

-- FUNCTIONS

-- MAIN PROGRAM

B. FLESH ON BONES

1. EXAMPLE

This example is intended to introduce you to input/output (IO).

The example is also a review for using:

if <condition> then <true-value> else <false-value>

Notice the type of the entire if/then/else construction in the example is IO ().

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

-- Module declaration.

module ReadAndShow where

-- import (No import required.)

--  Functions (No functions required.)

-- Main program

main :: IO ()
main = do
  putStrLn "Please type any number then press Enter or type 0 and press Enter to exit."
  inpStr <- getLine
   -- Keyed input is stored as a String in inpStr
  let inpDouble = (read inpStr) :: Double
   -- read changes inpStr type from String to Double.
  if inpDouble > 0
    then do
      putStrLn (show inpDouble ++ " multiplied by " ++ "2.0" ++ " is " ++ show (inpDouble * 2.0))
      -- show changes inpDouble type from Double to String.
      main
    else return ()

2. LOAD EXAMPLE

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

PS C:\Users\User> ghci
GHCi, version 9.4.8: https://www.haskell.org/ghc/  :? for help
ghci> :cd C:\Users\User\tinnel\Haskell 2024\newProjects
ghci> :l ReadAndShow.hs
[1 of 1] Compiling ReadAndShow      ( ReadAndShow.hs, interpreted )
Ok, one module loaded.

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

3. RUN EXAMPLE

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

ghci> main
Please type a number then press Enter or type 0 and press Enter to exit.
2.0
2.0 multiplied by 2.0 is 4.0
Please type a number then press Enter or type 0 and press Enter to exit.
0
ghci>

4. COMPILE EXAMPLE

As shown below, compile ReadAndShow.hs in GHC:

Prelude>  :! ghc --make "*ReadAndShow"
[1 of 1] Compiling Main             ( ReadAndShow.hs, ReadAndShow.o )
Linking ReadAndShow.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 to your program's editor, save the file and load it in GHCi.

Notice a module begins with the keyword module and is followed by the module name which is followed by the keyword where.

module ReadAndShow where

Also observe that using the arrow instead of the equal sign in the example shown below shows that getLine is not a real function and can return different values. This command means run the action getLine, and store the results we take in inpStr. Understanding the reason behind the choice of the word action instead of function is very important for your understanding of input/output (IO).

inpStr <- getLine

Other comments regarding this tutorial are discussed below:

1. Double

Haskell has a static type system. Everything in Haskell has a type. Types are labels, values carry, so we can reason about the values. In GHCi, the :t command, followed by any valid expression, tells us its type. Doing :t on an expression prints out the expression followed by :: and its type. :: is read as "has the type of".

Types have their own labels, called kinds. A kind is the type of a type. We can examine the kind of a type by using the :kind or :k command in GHCi. A * means that the type is a concrete type. A concrete type is a type that does not take any type parameters. Values can only have types that are concrete types. A * is pronounced "star" or "type".

We can see what the instances of a typeclass are, by using the :info command in GHCi. 

Prelude>  :kind Double
Double :: *
Prelude>

Prelude>  :info Double
data Double = D# Double# -- Defined in `GHC.Types'
instance Enum Double -- Defined in `GHC.Float'
instance Eq Double -- Defined in `GHC.Classes'
instance Floating Double -- Defined in `GHC.Float'
instance Fractional Double -- Defined in `GHC.Float'
instance Num Double -- Defined in `GHC.Float'
instance Ord Double -- Defined in `GHC.Classes'
instance Read Double -- Defined in `GHC.Read'
instance Real Double -- Defined in `GHC.Float'
instance RealFloat Double -- Defined in `GHC.Float'
instance RealFrac Double -- Defined in `GHC.Float'
instance Show Double -- Defined in `GHC.Float'
Prelude>  

2. read

Types are written in a capital case, so the a shown in the examples below cannot be a type. Since they are not in capital case they are actually type variables. That means that they can be of any type. Functions that have type variables are called polymorphic functions.

Prelude>  :type read
read :: Read a => String -> a
Prelude>

Prelude>  :info read
read :: Read a => String -> a -- Defined in `Text.Read'
Prelude>

The function read casts type String to another type.

3. show

Prelude>  :type show
show :: Show a => a -> String
Prelude>

Prelude>  :info show
class Show a where
  ...
  show :: a -> String
  ...
  -- Defined in `GHC.Show'
Prelude>

The function show casts a type to a readable type String.

Think of read and show as opposites: show lets you convert to String, and read converts from String.

4. :: type annotation

let inpDouble = (read inpStr)::Double

Type annotation is a way of explicitly saying what the type of an expression should be. We do that by adding :: at the end of the expression and then specifying a type.

II. CONCLUSION

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

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.



Tuesday, March 26, 2013

HASKELL BEGINNER PROGRAM SKELETON: "READ FILE WRITE FILE"

HASKELL BEGINNER PROGRAM SKELETON: "READ FILE WRITE FILE"

REVISED: Monday, October 21, 2024




Haskell Beginner Program Skeleton: A "Read File Write File" Tutorial.

I. READ FILE WRITE FILE EXAMPLE

Use your editor to save the following input.txt file:

A bug sat in a silver flower
thinking silver thoughts.
A bigger bug out for a walk
climbed up that silver flower stalk
and snapped the small bug down his jaws 
without a pause
without a care
for all the bug's small silver thoughts. 
It isn't right,
it isn't fair,
that that big bug ate that little bug
because that little bug was there.
He also ate his underwear. 

Also use your editor to save output.txt file as an empty file.

A. SKELETON

module Main where

import System.IO
import Data.Char

main :: IO ()
main = do 
       inh <- openFile "input.txt" ReadMode
       outh <- openFile "output.txt" WriteMode

       -- Your code goes here, replacing the function mainloop call.

       hClose inh
       hClose outh

-- And, your code goes here, replacing the function mainloop signature and definition.

B. FLESH ON BONES

1. SAVE

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

module Main where

import System.IO
import Data.Char(toUpper)

main :: IO ()     -- The main function signature.
main = do          -- Calls the main function. 
       inh <- openFile "input.txt" ReadMode
       outh <- openFile "output.txt" WriteMode
       mainloop inh outh                                -- Calls the mainloop function.
       hClose inh
       hClose outh

mainloop :: Handle -> Handle -> IO ()     -- The mainloop function signature.
mainloop inh outh =                                    -- The mainloop function definition.
    do ineof <- hIsEOF inh
       if ineof
           then return ()
           else do inpStr <- hGetLine inh
                   hPutStrLn outh (map toUpper inpStr)
                   mainloop inh outh

The above program is color coded: Variables are blue. Haskell keywords are red. User defined functions are orange.

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>

3. RUN

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

Prelude>  main
Prelude>

When you run main in GHCi it looks like nothing has happened; however, lots of things have happened. You have read from the input.txt file, and written to the output.txt file. At first it looks odd, because the program did not write output to your screen.

Open the output.txt file with your editor and you will see the following:

A BUG SAT IN A SILVER FLOWER
THINKING SILVER THOUGHTS.
A BIGGER BUG OUT FOR A WALK
CLIMBED UP THAT SILVER FLOWER STALK
AND SNAPPED THE SMALL BUG DOWN HIS JAWS 
WITHOUT A PAUSE
WITHOUT A CARE
FOR ALL THE BUG'S SMALL SILVER THOUGHTS. 
IT ISN'T RIGHT,
IT ISN'T FAIR,
THAT THAT BIG BUG ATE THAT LITTLE BUG
BECAUSE THAT LITTLE BUG WAS THERE.
HE ALSO ATE HIS UNDERWEAR. 

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. openFile

Prelude>  :type openFile
openFile :: FilePath -> IOMode -> IO Handle
Prelude>

inh <- openFile "input.txt" ReadMode

Opens the input.txt file in ReadMode and creates the input file handle inh. From this point in the program the input.txt file will be referred to by the name of its handle inh. For the time being, you can think of inh as a nickname for input.txt. 

outh <- openFile "output.txt" WriteMode

Opens the output.txt file in WriteMode and creates the output file handle outh. From this point in the program the output.txt file will be referred to by the name of its handle nickname outh.

2. ReadMode

Prelude>  :type ReadMode
ReadMode :: IOMode
Prelude>

inh <- openFile "input.txt" ReadMode

When openFile opens the input.txt file, ReadMode flags the file for read mode.

3. WriteMode

Prelude>  :type WriteMode
WriteMode :: IOMode
Prelude>

outh <- openFile "output.txt" WriteMode 

When openFile opens the output.txt file, WriteMode flags the file for write mode.

4. hClose

Prelude>  :type hClose
hClose :: Handle -> IO ()
Prelude>

       hClose inh
       hClose outh

hClose closes the input.txt file handle nicknamed inh; which is the same as closing the input.txt file.

hClose closes the output.txt file handle nicknamed outh; which is the same as closing the output.txt file. 

5. hIsEOF

Prelude>  :type hIsEOF
hIsEOF :: Handle -> IO Bool
Prelude> 

do ineof <- hIsEOF inh

hIsEOF inh returns True if no further input can be taken from inh; otherwise, it returns False. Then either True or False is bound to ineof by the <- function arrow. hIsEOF inh means execute hIsEOF inh in the IO monad and bind the result in ineof.

6. return

Prelude>  :type return
return :: Monad m => a -> m a
Prelude> 

In general, for any value, there is a computation which "does nothing", and produces that result. This is given by defining the function return for the given monad.

mainloop :: Handle -> Handle -> IO ()      -- The mainloop function signature.
mainloop inh outh =                                          -- The mainloop function definition.
    do ineof <- hIsEOF inh
       if ineof
           then return ()
           else do inpStr <- hGetLine inh
                   hPutStrLn outh (map toUpper inpStr)
                   mainloop inh outh

In our example the function mainloop has two arguments: inh and outh. The first do statement binds ineof with a True or False from hIsEof inh.

if in Haskell must always have a then and an else.

If ineof is True we are at the end of the input.txt file, then return () is executed and we return to the main function, close the input stream handle, close the output stream handle, and exit the main function.

If ineof is False the else second do first statement is executed binding inStr with the next line of characters from inh. The function map takes the function toUpper which takes a list of lower case characters inpSTR and produces a list of upper case characters which hPutStrLn writes to the output.txt file handle outh output stream. The second do third statement recursively calls the function mainloop with the two arguments inh and outh.

7. hGetLine

Prelude>  :type hGetLine
hGetLine :: Handle -> IO String
Prelude>

else do inpStr <- hGetLine inh

hGetLine reads a line from the input.txt file handle inh input stream and <- binds that line to inpStr.

8. hPutStrLn

Prelude>  :type hPutStrLn 
hPutStrLn :: Handle -> String -> IO ()
Prelude>

hPutStrLn outh (map toUpper inpStr) 

The function hPutStrLn writes a string to the output.txt file handle outh output stream and adds a newline character.

9. map

Prelude>  :type map
map :: (a -> b) -> [a] -> [b]
Prelude> 

hPutStrLn outh (map toUpper inpStr) 

In general, the function map takes any function of type (a -> b) and yields a function that takes a list of a's [a] and produces a list of b's [b]. In our example, the function map takes the function toUpper which takes a list of lower case characters inpSTR and produces a list of upper case characters which hPutStrLn writes to the output.txt file handle outh output stream.

10. toUpper

Prelude>  :type toUpper
toUpper :: Char -> Char
Prelude>

hPutStrLn outh (map toUpper inpStr)

The function toUpper converts a letter to the corresponding upper-case letter, leaving any other character unchanged. Any Unicode letter which has an upper-case equivalent is transformed. 

11. ()

Prelude>  :type ()
() :: ()
Prelude>

then return ()

In general, the () is an empty tuple, pronounced “unit”, indicating that there is no return value. In our example, then return () is executed and we return to the main function, close the input stream handle, close the output stream handle, and exit the main function.    

II. CONCLUSION

In this tutorial, you have received an introduction to the Haskell beginner program skeleton "read file write file".

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.