[Site Home] [Forum Home] [Articles] [File DB] [News Archives]

Programming -> A Gentle Haskell Intro, with pictures!


(View original topic)


Pyr-O-Rgasm - Sep-18-2005 server time
QUOTE (poser931 @ Sep 17 2005, 03:13 PM)
That is the reaction of most people when I tell them I ride a recumbent bicycle. Or they nod sagely and say, "Oh yes, my gym has one of those," visualizing me pedaling a stationary exercise bike. Then I explain that I ride a recumbent on the road, which confuses them even more. "it's a laid-back bicycle with a chair-like seat," I explain, "and I ride it because it’s fun, fast, and supremely comfortable." A comfortable bicycle? How radical!

Just nod your head and pretend you understand. huh.gif

Triple6_wild - Sep-17-2005 server time
what was the point of that post?

user posted image

Red Squirrel - Sep-17-2005 server time
huh?

poser931 - Sep-17-2005 server time
That is the reaction of most people when I tell them I ride a recumbent bicycle. Or they nod sagely and say, "Oh yes, my gym has one of those," visualizing me pedaling a stationary exercise bike. Then I explain that I ride a recumbent on the road, which confuses them even more. "it's a laid-back bicycle with a chair-like seat," I explain, "and I ride it because it’s fun, fast, and supremely comfortable." A comfortable bicycle? How radical!

Red Squirrel - Dec-24-2004 server time
Not much, just write what you would like it to look like as an actual article and send it to me by PM or email. The way it is here is good so if that's how you want it to look then I just need an ok to copy and paste it as an article. wink.gif

It's sent to quite a few sites to post so it will get quite allot of exposure.

wtd - Dec-23-2004 server time
What are the requirements for that?

Red Squirrel - Dec-23-2004 server time
Hey would you mind writing that as an article so it can be posted in the article section? Some useful info there. smile.gif

wtd - Dec-23-2004 server time
This is a repost of a tutorial posted at compsci.ca. Feel free to ask questions. smile.gif

Disclaimer

This is yet another attempt to bring the ideas of functional programming to the masses here, and an experiment in finding ways to make it easy and interesting to follow.

Your feedback would be good as a means of judging my progress.

Why should you care?

Functional programming is fundamentally a very different way of thinking about programming. After all, we can learn several languages and pat ourselves on the back, but if the only real difference is syntactic, then we're not really being challenged.

What do I need?

You'll need either a Haskell interpreter or compiler. For learning purposes the Hugs Haskell interpreter is fine.

You can download it from: http://www.haskell.org/hugs/

It installs as easily as any other Windows program, and an entry will be created in the Start menu under: Start -> Programs -> Hugs98.

If you're running Windows 98 or ME, it would probably be wise to restart your computer, just to make sure everything the installer did takes effect. Windows 2000 and XP are pretty good about immediately applying the changes.

Oh, and you'll want a good text editor. I suggest TextPad with the Haskell syntax coloring file. Directions for installing the syntax file are available.

A quick look at Hugs

Start -> Programs -> Hugs98 -> Nov2003 -> Hugs (Haskell98 mode)

user posted image

So, at startup of Hugs we've got some ASCII art, copyright information, some basic usage tips, and a prompt. This is a good start.

What can we do with this?

Well, we can evaluate expressions and see their results.

What's an expression?

An expression is a little bit of code that takes one or more values and gives you a new value.

Consider this very simple example.

user posted image

Moving on

But really, we could do basic math all day and be bored out of our skulls, so let's look at putting together a source file where we can build more complex things.

A Haskell source file is just a text file containing Haskell code. The extension we use is ".hs".

So, what will our source file contain? A simple hello world program.

CODE
module Main where

main = putStrLn "Hello, world!"


What do we have here?

Well, first of all we have to deal with the fact that Haskell code is organized into modules. Modules allow us to easily reuse code in other programs, and they allow the actual language itself to be relatively simple. The name of the file should match the name of the module. Here the module is named "Main".

Next we have the "main" function, the center of activity, as in many other programming languages. The ease of creating this function shouldn't come as any surprise given the fact that Haskell focuses on functions.

CODE
putStrLn "Hello, world!"


Here we simply use the putStrLn function to print a string to the screen on its own line. The similar putStr function does the same, but doesn't automatically skip to a new line.

user posted image

Testing the code

So, how do we run the code in this file?

Well, open up your trusty Command Prompt window and "cd" to the directory where you saved your Main.hs file.

Once you're there, start Hugs by simply typing "hugs" and hitting enter. Again we're back to:

user posted image

To load the "Main" module we simply:

CODE
Prelude> :load Main
Main>


The prompt has changed to indicate we're now in the Main module, rather than the Prelude module.

And to run the main function:

CODE
Main> main
Hello, world!

Main>


So, we've seen a little bit of Haskell

Is it scary?

If you say yes, that's not bad. New things can be scary. You'll get over it.

The real question, though, is: where do we go from here?

Well, since Haskell is a functional programming language, I'm thinking it might be good to see some more functions.

Expanding on Hello, world

Anyone 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:

CODE
module Main where

main = putStrLn "Hello, world!"


Now we're going to expand it with:

CODE
module Main where

main = greet "Bob"

greet name = putStrLn ("Hello, " ++ name ++ "!")


Of course, what if we want just the greeting string?

CODE
module Main where

main = greet "Bob"

greet name = putStrLn (greeting name)

greeting name = "Hello, " ++ name ++ "!"


Tidying the code up - Haskell tricks

Haskell 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:

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

CODE
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."

CODE
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:

CODE
greet name = putStrLn $ greeting name


So, our code now looks like:

CODE
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 output

All 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".

CODE
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:

CODE
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:

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

Conditionals

How about when the name given to the greeting function is "Haskell", the greeting is "Hey, whadda ya know? This is a Haskell program!"

CODE
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 expressions

Let's say we want our program to greet "Matz" with, "You make a good language." Using only "if":

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

CODE
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 functions

Of course, we can do this even more cleanly by overloading the greeting function.

CODE
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 ++ "!"


Loops

At this point you might be tempted to ask how Haskell handles looping, since that's a pretty basic thing for programmers to learn about in other languages.

Haskell provides no special syntax for looping. All looping is achieved via recursion, where a function calls itself.

CODE
module Main where

main :: IO ()
main = do putStr "You are? "
         name <- getLine
         if name == "quit"
           then return ()
           else do greet name
                   main

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 ++ "!"


A few questions that may come up from looking at this:
  • What does
CODE
return ()
do? This basically turns () into IO (), which is the return our main function wants.
  • Why is "do" repeated in the "if" expression? The "if" basically "interrupts the chain of expressions we were creating. To start another "chain" we need to use "do" again.

  • (Showing 50 last posts, newest on top)