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


There's a simpler way

The task of applying a function to each element in a list is such a common one, you'd think there would be a function or functions already present to solve this problem.

When we're applying a normal function, we can use "map". Let's say we want to double each number in a list.

timesTwo x = x * 2

map timesTwo [1,2,3,4]

Now, when we're using IO "actions", we can't use map, but rather we use the mapM_ function.

module Main where

main :: IO ()
main = do names <- greetMultiple
          putStrLn "I greeted:"
          mapM_ putStrLn names

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

greeting :: String -> String
greeting "Haskell" = "Hey, whadda ya know?  This is a Haskell program!"
greeting "Matz"    = "You make a good language."
greeting name      = "Hello, " ++ name ++ "!"

greetMultiple :: IO [String]
greetMultiple = do putStr "You are? "
                   name <- getLine
                   if name == "quit"
                     then return []
                     else do greet name
                             nextRun <- greetMultiple
                             return $ name : nextRun

A different greeting approach

Now, lets say we first want to gather names from a group and then greet them all. First we need a gatherNames function.

gatherNames :: IO [String]
gatherNames = do putStr "You are? "
                 name <- getLine
                 if name == "quit"
                   then return []
                   else do otherNames <- gatherNames
                           return $ name : otherNames

Now we can incorporate that into our program and use map to generate the appropriate greetings, then pass the result of that with $ to the "mapM_ putStrLn" we've already seen.

module Main where

main :: IO ()
main = do names <- gatherNames
          mapM_ putStrLn $ map greeting names

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

greeting :: String -> String
greeting "Haskell" = "Hey, whadda ya know?  This is a Haskell program!"
greeting "Matz"    = "You make a good language."
greeting name      = "Hello, " ++ name ++ "!"

gatherNames :: IO [String]
gatherNames = do putStr "You are? "
                 name <- getLine
                 if name == "quit"
                   then return []
                   else do otherNames <- gatherNames
                           return $ name : otherNames

Back to printAll

mapM_ putStrLn names

Of course we may look at this and think it's uglier in use than:

printAll names

But previously we've seen that the definition of printAll adds quite a bit of unnecessary code. So, let's redefine printAll.

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

Much better. We can improve this further, though. In Haskell, if we have a function which takes two arguments, and only give it one of those arguments, we get another function which takes the final argument.

This is known as "partial application." The partial application of functions allows us to easily create new functions based on already existing functions. The printAll function can be rewritten as:

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

Similarly we can generate the greetings like so:

greetings :: [String] -> [String]
greetings = map greeting

Now, our code looks like:

module Main where

main :: IO ()
main = do names <- gatherNames
          printAll $ greetings names

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

greeting :: String -> String
greeting "Haskell" = "Hey, whadda ya know?  This is a Haskell program!"
greeting "Matz"    = "You make a good language."
greeting name      = "Hello, " ++ name ++ "!"

greetMultiple :: IO [String]
greetMultiple = do putStr "You are? "
                   name <- getLine
                   if name == "quit"
                     then return []
                     else do greet name
                             nextRun <- greetMultiple
                             return $ name : nextRun

gatherNames :: IO [String]
gatherNames = do putStr "You are? "
                 name <- getLine
                 if name == "quit"
                   then return []
                   else do otherNames <- gatherNames
                           return $ name : otherNames

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

greetings :: [String] -> [String]
greetings = map greeting

Combining data

In many cases, data isn't as simple as just someone's name. We often want different pieces of data grouped together into a single unit. In Haskell we accomplish his via tuples.

So, with a name, let's say we want to store an age as well, so our greeting function can come up with greetings based on age as well. An example tuple, then, might be:

("Bob", 49)

But, first, we need to be able to read an integer from the user. Since everything read in is in the form of a string, we need a means to extract an integer from a string. The "reads" function steps in. Now, reads returns something along the lines of:

[(Int, String)]

As a result we need to get the first element from the array, and the first item from the tuple. The !! operator and fst function will do these things quite nicely.

So, a basic getAge function might look like:

getAge :: IO Integer
getAge = do putStr "And you're how old? "
            input <- getLine
            return $ fst $ reads input !! 0

There's just one problem with this, and it's a big one. What if someone gives you bad input? Well, then reads will gives us am empty list, and that makes "reads input !! 0" cause an error.

We need a way to avoid the error in the first place. We can do so by adding in a conditional expression which checks to see if the list is empty. Of course, if it is, we should probably tell the user we didn't get their age, and ask them again. Recursion will serve us well here.

getAge :: IO Int
getAge = do putStr "And you're how old? "
            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

Taking as step back again

We want to not only get names, but also their ages. Where we had a gatherNames function before, let's replace that with a gatherInfo function.

gatherInfo :: IO [(String, Int)]
gatherInfo = do putStr "You are? "
                name <- getLine
                if name == "quit"
                  then return []
                  else do age <- getAge
                          let info = (name,age)
                          otherInfo <- gatherInfo
                          return $ info : otherInfo

The basic form looks pretty much like other recursive functions we've defined.

Now, even though we have a very different type of data here than a simple string, we can get the string from each piece of info with map and fst.

module Main where

main :: IO ()
main = do info <- gatherInfo
          let names = map fst info
          printAll $ greetings names

greeting :: String -> String
greeting "Haskell" = "Hey, whadda ya know?  This is a Haskell program!"
greeting "Matz"    = "You make a good language."
greeting name      = "Hello, " ++ name ++ "!"

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

greetings :: [String] -> [String]
greetings = map greeting

getAge :: IO Int
getAge = do putStr "And you're how old? "
            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 [(String, Int)]
gatherInfo = do putStr "You are? "
                name <- getLine
                if name == "quit"
                  then return []
                  else do age <- getAge
                          let info = (name,age)
                          otherInfo <- gatherInfo
                          return $ info : otherInfo

Doing something with the extra information

Now we have both name and age info for each of the people we're greeting, and we can still greet people the old way, but that seems to be missing the point of having that extra information in the first place. So, we want to get different greetings depending on age, as well as name.

So, we'll first want to remake the greeting function. Our old greeting function looked like:

greeting :: String -> String
greeting "Haskell" = "Hey, whadda ya know?  This is a Haskell program!"
greeting "Matz"    = "You make a good language."
greeting name      = "Hello, " ++ name ++ "!"

The first thing we realize will have to change is the signature of this function.

greeting :: (String,Int) -> String

Now, for the rest, if the age is less than twelve, we'll answer with: "Do your parents know where you are, ?" If the age is greater than eighty, we'll answer with: "Do your children know where you are, ?" Otherwise we'll use the same rules we applied in the previous greeting function.

To this end we'll use "guards" to define multiple versions of the function for these different conditions. The syntax should be fairly obvious.

greeting :: (String,Int) -> String
greeting (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 ++ "!"

So, our entire program is now:

module Main where

main :: IO ()
main = do info <- gatherInfo
          printAll $ greetings info


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

gatherNames :: IO [String]
gatherNames = do putStr "You are? "
                 name <- getLine
                 if name == "quit"
                   then return []
                   else do otherNames <- gatherNames
                           return $ name : otherNames

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

greetings :: [(String,Int)] -> [String]
greetings = map greeting

getAge :: IO Int
getAge = do putStr "And you're how old? "
            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 [(String, Int)]
gatherInfo = do putStr "You are? "
                name <- getLine
                if name == "quit"
                  then return []
                  else do age <- getAge
                          let info = (name,age)
                          otherInfo <- gatherInfo
                          return $ info : otherInfo

greeting :: (String,Int) -> String
greeting (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 ++ "!"






Next Page
spacer
30756 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