|
|
Send spam to: website@xeonlive.com nick@xeonlive.com georgiapeach1241@aol.com Functional programming with Haskell By Chris Dutton Expanding on Hello, worldAnyone even moderately familiar with my other tutorials will recognize the pattern of starting with a simple hello world program and then expanding upon it. So, let's create a function "greet" which takes a name and greets that person. Our Main.hs looked like: module Main where main = putStrLn "Hello, world!" Now we're going to expand it with: module Main where main = greet "Bob" greet name = putStrLn ("Hello, " ++ name ++ "!") Of course, what if we want just the greeting string? module Main where main = greet "Bob" greet name = putStrLn (greeting name) greeting name = "Hello, " ++ name ++ "!" Tidying the code up - Haskell tricksHaskell by default infers the types of data being used in a program, but for documentation purposes, we can explicitly specify the types a function uses and returns. Doing this, we write a type signature for main. The "main" function does an IO action, and returns the closest thing you can get in Haskell to nothing, so: module Main where main :: IO () main = greet "Bob" greet name = putStrLn (greeting name) greeting name = "Hello, " ++ name ++ "!" Double colons separate the name of a function and its signature. Continuing, we generate a signature for the "greet" and "greeting" functions. module Main where main :: IO () main = greet "Bob" greet :: String -> IO () greet name = putStrLn (greeting name) greeting :: String -> String greeting name = "Hello, " ++ name ++ "!" These signatures are saying, "greet takes a string as an argument and does an IO operation," and, "greeting takes a string as an argument and returns another string." greet name = putStrLn (greeting name) Here we use parentheses because otherwise this would be seen as putStrLn taking two arguments, "greeting" and "name". Since putStrLn only takes one argument, this would clearly be erroneous. But the parentheses can get annoying, so we have the $ operator. Essentially, the $ operator takes the value on its right hand side and gives it to the function on the left hand side. So, now our greet function looks like: greet name = putStrLn $ greeting name
So, our code now looks like: module Main where main :: IO () main = greet "Bob" greet :: String -> IO () greet name = putStrLn $ greeting name greeting :: String -> String greeting name = "Hello, " ++ name ++ "!" Input as well as outputAll of this greeting isn't very much good unless we can get input from the user as well, to find out their name. IO actions aren't quite like other functions. To "chain" them together in sequence we use the keyword "do". module Main where main :: IO () main = do putStr "You are? " name <- getLine greet name greet :: String -> IO () greet name = putStrLn $ greeting name greeting :: String -> String greeting name = "Hello, " ++ name ++ "!" Probably the most immediately notiecable change is the use of indentation. Haskell uses what's referred to as "layout", so semi-colons and braces aren't necessary. They are available: main = do { putStr "You are? "; name <- getLine; greet name } Of course, the "layout" approach is so much nicer that it'd be silly to use braces and semi-colons. The second new bit of syntax is: name <- getLine
The return of getLine is a string, but an IO "tainted" string, which can't be immediately used. Using the <- syntax, "name" is a plain old string we can use elsewhere. ConditionalsHow about when the name given to the greeting function is "Haskell", the greeting is "Hey, whadda ya know? This is a Haskell program!" module Main where main :: IO () main = do putStr "You are? " name <- getLine greet name greet :: String -> IO () greet name = putStrLn $ greeting name greeting :: String -> String greeting name = if name == "Haskell" then "Hey, whadda ya know? This is a Haskell program!" else "Hello, " ++ name ++ "!" This should look fairly straightforward to a programmer with basic experience in other languages. Also, again we use "layout" to signify the structure of the conditional. Case expressionsLet's say we want our program to greet "Matz" with, "You make a good language." Using only "if": module Main where main :: IO () main = do putStr "You are? " name <- getLine greet name greet :: String -> IO () greet name = putStrLn $ greeting name greeting :: String -> String greeting name = if name == "Haskell" then "Hey, whadda ya know? This is a Haskell program!" else if name == "Matz" then "You make a good language." else "Hello, " ++ name ++ "!" Wow, that's ugly. Fortunately, we have the case expression that should look familiar to programmers. module Main where main :: IO () main = do putStr "You are? " name <- getLine greet name greet :: String -> IO () greet name = putStrLn $ greeting name greeting :: String -> String greeting name = case name of "Haskell" -> "Hey, whadda ya know? This is a Haskell program!" "Matz" -> "You make a good language." otherwise -> "Hello, " ++ name ++ "!" As with "if", we use layout. Overloading functionsOf course, we can do this even more cleanly by overloading the greeting function. module Main where main :: IO () main = do putStr "You are? " name <- getLine greet name 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 ++ "!" Next Page
|
![]() |
This site best viewed in a W3C standard browser at 800*600 or higher Site design by Red Squirrel | Contact © Copyright 2010 Ryan Auclair/IceTeks, All rights reserved |