Not Weakly Typed
The first lesson may have led you to believe Haskell is weakly typed. After all, there were no type declarations anywhere. In fact, Haskell is implicitly typed, inferring types based on usage and then strongly enforcing.
The problem is that foo is inferred to be of type Num (i.e. numeric), but a [Char] is needed by operator++. We can easily fix this by replacing foo(3) with show(foo(3)).
A Type Problem
Here is a less intuitive problem. This code works...
... and so does this ...
... but this fails.
What happened here? Haskell treats the numeric constant "3" as being of type Num. This is a type which Haskell is able to convert either to an Integral (i.e. an integer of some sort) or Floating (i.e. a real of some sort). In int.hs, the "m = mod d 2" causes Haskell to infer the type of "d" to be Integral. In doub.hs, "s = sqrt d" causes Haskell to infer the type of "d" to be Floating. If both are present, Haskell does not know what to do.
Here the function fromIntegral() converts an Integral back to a Num. This value can now be interpreted as a Floating when it is used by sqrt(). The symbol d can now safely be inferred to be of type Integral, and Haskell is happy.
Declaring Function Types
It is also possible to declare the type of a function, avoiding Haskell's implicit typing system.
The line "foo:: Int -> Int" means the function foo takes an Int and returns an Int. What happened to Num and Integral?
The types Num, Integral, and Floating in Haskell are not really concrete types, but something called classes. These are similar to Java's interfaces and just describe the set of operators and functions that apply to one of several concrete types.
The standard concrete types matching the Integral class are Int (a fixed precision integer) and Integer (an arbitrary precision integer).
By making the declaration above, we've chosen to have the function apply only to fixed precision integers. Had we left the definition off, Haskell would have used a template definition that would allow any concrete type matching the Integral class to work.
Above we see an example of the template definition based on Num. The lower case name "a" is the template parameter.
The line "pow1:: Int->Int->Int" means that pow1 takes an Int and returns a function that takes an Int and returns an Int.
This example compares a fixed to a finite precision version of the pow function.
One way to learn about the types of functions is to use Hugs, the Haskell interpreter. Just type "ghci yourfile.hs" and when Hugs starts you can type ":t myfunc" and Hugs will show you the type declaration for your function as inferred by Haskell.
To help you find your way throught the many types and declarations of the Haskell world, please use Hoogle (http://www.haskell.org/hoogle/).