Misc Links
Forum Archive
News Archive
File DB
 

Ads
 

Advertisement
 

Latest Forum Topics
wow 56 k modems are
Posted by Red Squirrel
on Oct 14 2013, 11:52:23 pm

I Need A Program
Posted by rovingcowboy
on Sep 23 2013, 5:37:59 pm

having trouble witn lan
Posted by rovingcowboy
on Sep 23 2013, 5:40:56 pm

new problem for me
Posted by rovingcowboy
on Sep 23 2013, 5:54:09 pm

RBC Royal Bank
Posted by Red Squirrel
on Aug 13 2013, 6:48:08 pm

 

Introduction to Haskell
Functional programming with Haskell
By Chris Dutton


Compilation

At this point you might be thinking that this is all well and good, but this method of interpretation, where we write the code, then test it is tedious and not really suited to writing a program that can run, do its thing, then quit.

Fortunately, Haskell can be compiled to native code as well as interpreted.

The compiler we'll use is the Glorious Glasgow Haskell Compilation System, or just GHC, available at:

http://www.haskell.org/ghc/

An installer for Windows is available at:

http://www.haskell.org/ghc/download_ghc_622.html#windows

To compile the program we've assembled, the command is:

prompt> ghc -O --make Main -o greeter.exe

The -O tells the compiler to perform optimizations, --make tells the compiler we're actually building an executable based on this module, and -o tells the compiler the name of the resulting executable.

Now, we can run this program.

prompt> greeter

And we see something rather odd. Nothing.

Why don't we see anything?

Well, it's a petty little thing which has nothing to do with functional programming. The problem is that Haskell programs compiled with GHC don't output anything until a newline is entered. When we ask, "You are?", we don't go to a new line, so nothing gets printed to the screen.

The fix is simple. We need to flush all of the text in standard output. For this we'll need to import the IO module, then flush standard output after every instance of putStr. The putStrLn function doesn't have this problem.

With this taken into mind, our Greeting module now looks like:

module Greeting where

import qualified Char (toLower)
import IO

data PersonInfo = Person String Int

class Greetable a where
  -- declarations
  greeting  :: a -> String
  greetings :: [a] -> [String]
  greet     :: a -> IO ()
  -- default definitions
  greetings =  map greeting
  greet x   =  putStrLn $ greeting x

instance Greetable PersonInfo where
  greeting (Person name age)
    | age < 12          = "Do your parents know where you are, "  ++ name ++ "?"
    | age > 80          = "Do your children know where you are, " ++ name ++ "?"
    | name == "Haskell" = "Hey, whadda ya know?  This is a Haskell program!"
    | name == "Matz"    = "You make a good language."
    | otherwise         = "Hello, " ++ name ++ "!"

printAll :: [String] -> IO ()
printAll = mapM_ putStrLn

getAge :: IO Int
getAge = do putStr "And you're how old? "
            hFlush stdout
            input <- getLine
            let parsed = reads input
            if parsed == []
              then do putStrLn "I'm sorry, but could you repeat that?"
                      getAge
              else return $ fst $ parsed !! 0

gatherInfo :: IO [PersonInfo]
gatherInfo = do putStr "You are? "
                hFlush stdout
                name <- getLine
                if name == "quit"
                  then return []
                  else do age <- getAge
                          let info = Person name age
                          otherInfo <- gatherInfo
                          return $ info : otherInfo

data Build = Skinny | Medium | Fat
data Color = White | Black | Gray | Red | Brown deriving (Show)
data DogInfo = Dog String Build Color

instance Greetable DogInfo where
  greeting (Dog _    Skinny _    ) = "There's hardly anything to that dog."
  greeting (Dog _    Fat    _    ) = "What a fat dog!"
  greeting (Dog name _      color) = "What a healthy " ++
                                     (map Char.toLower $ show color) ++
                                     " dog.  Hello, " ++ name ++ "!"

Now, repeating the above mentioned compiling will give us an executable that does the right thing.

More on executables

Now that we have the ability to compile a Haskell program to a native executable, there are a couple of things we might want to do.

  • Execute another program from within our own.
  • Get arguments supplied to our program.

Both are provided by functions in the System module. The first is solved by the system function. The most useful example of this is probably to run the Windows "pause" program, which creates that famous "press any key to continue..." message.

This is useful for preventing a console window from instantly closing when the program has finished its work. If I compile and run the following by double-clicking on the resulting executable, t'll print the message, but it'll do it so quick that the window will close before I can see anything.

module Main where

main :: IO ()
main = putStrLn "Hello, world!"

Instead, I want to pause at the end of the program, so I compile:

module Main where

import System

main :: IO ExitCode
main = do putStrLn "Hello, world!"
          system "pause"

Interestingly, the system function returns an ExitCode, so you can tell if the program you ran was successful or not. If it was successful, ExitSuccess is returned. Otherwise, ExitFailure is returned. The ExitFailure constructor also takes an int, so you can tell exactly what code the program exited with.

We could test the success of pause.

module Main where

import System

main :: IO ExitCode
main = do putStrLn "Hello, world!"
          result <- system "pause"
          case result of
            ExitSuccess   -> putStrLn "It worked."
            ExitFailure x -> putStrLn $ "Failure: " ++ show x
          system "pause"

Now, I mentioned getting variables passed to the program. The getArgs function will do that, in the form of a list of strings. So, let's greet the names passed to a program.

module Main where

import System

main :: IO ExitCode
main = do names <- getArgs
          let greetings  = map greeting names
              printAll   = mapM_ putStrLn
              greeting n = "Hello, " ++ name ++ "!"
          printAll greetings
          system "pause"

And now for something completely different

Let's build a quick replacement for the typical copy program, which takes one file and copies its contents into another file.

This should cover:

  • Files
  • Error handling

To get at the file handling functions, we'll need the IO module.

Let's take a look at opening a file and reading the contents, then printing them to the screen.

module Main where

import IO

main :: IO ()
main = do fh <- openFile "bar.txt" ReadMode
          text <- hGetContents fh
          hClose fh
          putStrLn text

Dissecting that...

openFile "bar.txt" ReadMode

The first argument is obviously the name of the file to open. The second argument is the mode in which to open the file. We could have also chosen WriteMode, AppendMode, or ReadWriteMode.

hGetContents fh

Obviously this gets the contents of "bar.txt".

hClose fh

Here we're closing the file.

So, now we can open the file, get the contents, and close the file. The next goal is to be able to open a file, write to it, and close it.

module Main where

import IO

main :: IO ()
main = do fh <- openFile "foo.txt" WriteMode
          hPutStr fh "Hello\nworld!"
          hFlush fh
          hClose fh

There's not a whole lot new here.

Simplifying the handling of files

But if this is all we're doing with files, we can replace all of that with two functions: readFile and writeFile.

So, let's write a program that copies one file's contents into another file.

module Main where

import IO

main :: IO ()
main = do text <- readFile "foo.txt"
          writeFile "bar.txt" text

Arguments

Of course, to more accurately emulate the copy command, our program should work on user-supplied filenames, rather than hard-coded ones.

module Main where

import IO
import System

main :: IO ()
main = do args <- getArgs
          let i = args !! 0
              o = args !! 1
          text <- readFile i
          writeFile o text

Error handling

If we supply less than two arguments, this will fail due to "o = args !! 1". So we should check to see if there are at least two arguments. If not, remind the user.

module Main where

import IO
import System

main :: IO ()
main = do args <- getArgs
          if length args >= 2
            then let i = args !! 0
                     o = args !! 1
                 text <- readFile i
                 writeFile o text
            else hPutStrLn stderr
                           "This program needs at least two arguments."

Now, if we try to open a file for reading that doesn't exist, our program will raise an error and terminate. We could try to prevent this, but that's pretty tedious, so instead we'll let the error happen and "catch" it, using, appropriately enough, the catch function.

Our desired behavior is, if the file to be read doesn't exist, the text should be an empty string. The catch functions allows us to do that by providing a function to run if an error occurs.

module Main where

import IO
import System

main :: IO ()
main = do args <- getArgs
          if length args >= 2
            then let i = args !! 0
                     o = args !! 1
                 text <- catch (readFile i)
                               (\_ -> return "")
                 writeFile o text
            else hPutStrLn stderr
                           "This program needs at least two arguments."

The only thing that should look odd here is:

\_ -> return ""

What we're seeing here is another way of writing a function. The backslash introduces the function's arguments, and the -> leads to the actual guts of the function. The argument in this case is the error itself, but we aren't concerned with what the error actually is, so we use an underscore. The function itself simply returns an empty string.

One last thing

In the last code sample, we saw:

catch (readFile i)
      (\_ -> return "")

What you might also see is the use of:

readFile i `catch` \_ -> return ""

Here the backticks around catch turn a function with two arguments into an operator.

Which of these two forms you use is purely a matter of style, but it's important to understand either.

Chris Dutton
cdutton@gmail.com





spacer
30787 Hits Pages: [1] [2] [3] [4] [5] [6] 6 Comments
spacer


Latest comments (newest first)
Posted by genea on December 12th 2005 (13:14)
Very good article, with the part about type classes and the ability for a data type derived from a given class being able to automatically inherit the method (functions) from the class definition. I guess I missed that in the mountain of literature I had read and had been up till now missing the benefit of this info... I liked dobbing about with HASKELL and this just makes me believe, that it will have a bright future ahead, or one of the languages derived from it, such as CLEAN.
Thanks again.. and I too would like to see the next six!!
gene

spacer
Posted by wtd on June 06th 2005 (19:10)
QUOTE (HHH @ Jun 4 2005, 12:40 PM)
Hi,
how can I do a line skip in a String. I mean that one String ouput looks like this:

Hello, (line skip)
....

CODE
putStrLn "Hello"


The "Ln" means "line".

If you just want to skip a line...

CODE
putStrLn ""


Or you could make that a function.

CODE
skipLine = putStrLn ""

spacer
Posted by wtd on January 01th 2005 (16:44)
QUOTE (fred laforge @ Jan 11 2005, 10:03 AM)
great tutorial -- when will you post the next 6 lessons? :-)

Heh. That one took a bit out of me, so there might just be smaller updates for a while. smile.gif.

spacer
Posted by wtd on January 01th 2005 (02:12)
In my introduction to Haskell I createded a simple function greeting, which takes a name as a string and formulates a greeting.

CODE
greeting :: String -> String
greeting name = "Hello, " ++ name ++ "!"


And then, I created a greet function which takes a name and prints the greeting for it.

CODE
greet :: String -> IO ()
greet name = putStrLn (greeting name)


The latter function I rewrote as:

CODE
greet :: String -> IO ()
greet name = putStrLn $ greeting name


Now, the argument "name" should appear quite redundant in that last example. It is. What if, instead, we could simply combine the two functions, "greeting" and "putStrLn".

Haskell provides an easy mechanism for doing so.

CODE
greet :: String -> IO ()
greet = putStrLn . greeting


Now, we can make a similar observation about the greeting function.

CODE
greeting :: String -> String
greeting name = "Hello, " ++ name ++ "!"


This is really two functions, since operators are just functions. Given the order of evaluation, this could be rewritten:

CODE
greeting :: String -> String
greeting name = "Hello, " ++ (name ++ "!")


Now, since we can partially apply functions - give them one argument, and get back a function which takes another argument and gives us the rest - we can rewrite this as:

CODE
greeting :: String -> String
greeting = ("Hello, " ++) . (++ "!")


Just as in the original, the function takes a string, appends "!" to it, then prepends "Hello, " to that.

spacer
Posted by wtd on January 01th 2005 (19:54)
Oh, and when you get around to writing something in Haskell, and you have questions, feel free to ask. smile.gif
spacer
View all comments
Post comment


Top Articles Latest Articles
- What are .bin files for? (669062 reads)
- Text searching in linux with grep (161180 reads)
- Big Brother and Ndisuio.sys (150471 reads)
- PSP User's Guide (139547 reads)
- SPFDisk (Special Fdisk) Partition Manager (117240 reads)
- How to Use MDADM Linux Raid (188 reads)
- What is Cloud Computing? (1225 reads)
- Dynamic Forum Signatures (version 2) (8769 reads)
- Successfully Hacking your iPhone or iTouch (18714 reads)
- Ultima Online Newbie Guide (35906 reads)
corner image

This site best viewed in a W3C standard browser at 800*600 or higher
Site design by Red Squirrel | Contact
© Copyright 2019 Ryan Auclair/IceTeks, All rights reserved