The Easy PartHello, World!The first step in learning any programming language is the 'hello, world' program. Here's the Haskell version.
In Haskell, as in C, you have to define "main" in order to create an executable. Haskell, however, does not use the curly braces. Numbers
To obtain numeric output, it is necessary for us to convert the result to a string in order to print it. We do that with show(). String Concatenation
String concatenation is accomplished with the ++ operator. Let Blocks
In order to set variables you need to use a "let" block. You'll notice that both x and z depend on y, but y is defined after x and before z. This is possible because Haskell is a functional language. That means that variables are defined when they are set, and never change in value. That means that order of assignment is not important. The other thing to notice is that blocks are defined by whitespace, just as they are in python. If you prefer to use curly braces, like C or java, this is possible. Defining functions
Here we see our first example of a function that takes an argument. In this case, it is the inefficient implementation of the Fibonacci function. One important thing to note here is that function names are never capitalized. Capitalized identifiers must refer to type names in Haskell.
Here we see a slightly prettier version of the Fibonacci using what are called "patterns" to handle the condition where n < 2.
Another variant, called "guards" can be used to do the same thing. One of the characteristics of the function defined above is that it is referentially transparent. This means that if a function is called twice with the same arguments, the compiler is free to replace the second function call with the value obtained by the first. Furthermore, there is no way to determine what order fib(n-1) and fib(n-2) will evaluate. The compiler is free to attempt either computation first. Lazy Evaluation
Haskell is lazy. That means, it doesn't compute a value if it does not have to. In this case, neither the variables f1000 nor hi are computed. Fib3 evaluates as quickly as fib2, and does not print the word "hello." More than one line of output
In order to do more than one line of output, you need to use a "do" block. It is a special construct for stitching together a number of output commands. Inside a do block, each command will execute independently and in order. This gets around all the limitations imposed by functional programming. Note also the use of putStr() vs. putStrLn(). The putStr() function does not output a carriage return / line feed. Loops
Here we put all the information above together to teach you how to write a simple loop. Because the if statement was outside the do block we were forced to write the putStrLn("n="...) line twice.
That's still not elegant. However, there is another option. Case statements are allowed inside do blocks:
The tricky thing about case statements is that their patterns must be compile time constants. You can't use "nhi ->" in your code. That's why we chose to switch on (nhi-nlo) rather than nlo. Of course, we could have simply inverted the if and do blocks like so:
RecursionIf you are a C programmer, using recursion may worry you. The following C program would probably seg fault because it would completely exhaust the stack:
The equivalent Haskell program, however, is smart enough not to grow the stack because it knows that it is not really necessary since the above code is logically equivalent to a loop.
Changing StateIf you still want think imperatively, you can just use a function call whenever you normally use a state change.
These loop and state examples probably make you think that Haskell is a cumbersome difficult language -- but really, this is not illustrative of the Haskell way of doing things. It is more like the C way of doing things shoe-horned into Haskell. But hey, we have to start somewhere.
|