1

I have recently started learning Haskell in lectures, but when I try to find further explanations to understand how it works online, I have found the code looks completely different. Note: I have only learnt C before and am used to most code following similar formats, and looking similar.

For example, a function for the factorial of a number in my lectures looks like this:

{-**********-}

fac :: Int -> Int
fac n
    | n==0  = 1
    | n>0   = n * fac (n-1)

{-**********-}

but when I look online it looks completely different, and far more simple. For example:

factorial 1 = 1
factorial k = k * factorial (k-1)

Can anybody explain why the code I am being taught is more effective, in particular where it says

fac:: Int->Int 

and why that matters?

Cirdec
  • 24,019
  • 2
  • 50
  • 100
Elis Jones
  • 327
  • 2
  • 15
  • 4
    As the answers have pointed out, these are different ways of writing essentially the same function. It might be worth pointing out that the `fac` and `factorial` definitions are not the same function, they have different meanings when applied to `0`. Here are a few more ways to solve the same problem (mostly a joke) http://www.willamette.edu/~fruehr/haskell/evolution.html – Cirdec Dec 04 '14 at 18:12
  • @Cirdec Nice link. It can be very educational :) – Eugene Sh. Dec 04 '14 at 18:23
  • The first is less efficient because it does an extra pointless comparison (`n>0`). – user2407038 Dec 04 '14 at 20:01
  • The first one is undefined for numbers less than zero and will exit with an error. The second one will go into an infinite loop for numbers less than zero. – Tim Perry Dec 04 '14 at 23:35

4 Answers4

3

The line you are asking about is the function type signature, much like function prototype declaration in C. The specific line says that the function fac is taking an Int parameter and returning Int result. This is not always necessary in Haskell, since it can deduce types out of context the function is used. The other differences are just different methods to handle cases. The first one is called guards, the second is pattern matching.
Generally I would strongly recommend this resource for easy and fun Haskell learning: Learn You a Haskell for Great Good!

Upd:
Just a brief explanation why in this specific case the type signature is essential. Asssume having two different functions as you stated, one with signature and the second without:

factorial1 :: Int -> Int
factorial1 n
    | n==0  = 1
    | n>0   = n * factorial1 (n-1)


factorial2 1 = 1
factorial2 k = k * factorial2 (k-1)

Then if we will try to do something like factorial1 3.5, haskell will throw an error, since it knows that 3.5 is not an Int. But for factorial2 haskell just knows that we are working with numbers, and 3.5 is just fine. But for input 3.5 it will end up in an infinite loop, since it will never get to the boundary condition (k=1). This is why sometimes signatures are essential. Sometimes in opposite you would like your function to be generic for different types, but in that case you still should have some signature defining some more general type constraints (haskell has such a facility).

Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61
  • Ok, thanks, that makes sense. Is there any benefit to declaring it if it can be deduced anyway? Or rather are there any negative affects of not declaring it? – Elis Jones Dec 04 '14 at 17:53
  • @ElisJones it's easier to generate documentation from functions with explicit sigs, and some type errors are easier to solve if you narrow the types manually. I typically just add sigs on module boundaries. – Bartek Banachewicz Dec 04 '14 at 17:55
  • @ElisJones There is a similar question here: http://stackoverflow.com/questions/27067905/when-are-type-signatures-necessary-in-haskell but it is talking about some advanced stuff that you probably have difficulty understanding at this stage – Eugene Sh. Dec 04 '14 at 17:58
  • @ElisJones Just added some example to the answer that is addressing (briefly and incompletely) this question – Eugene Sh. Dec 04 '14 at 18:18
  • @ElisJones type signatures are sometimes required by Haskell in the face of ambiguity; for example as you start writing simple scripts you may find yourself writing `readLn :: IO Double` versus `readLn :: IO Int` versus `readLn :: IO Integer` -- what's happening here is that Haskell can read the user input`"3"` either as a double-precision floating-point number or a system-word-length signed integer or as an arbitrarily-large signed integer. But thinking about types also helps in writing a function; by saying "what parameters am I expecting?" you limit what you can do with them, which is good. – CR Drost Dec 04 '14 at 21:28
0
fac:: Int->Int 

is the type signature of the function. It takes a argument of type Int and returns an Int.

These are useful for documentation.

Haskell is able to infer types on its own, see this link: https://www.haskell.org/haskellwiki/Type_inference

"Type inference is a feature of the type system which means that concrete types are deduced by the type system whereever it is obvious"

This means we don't have to provide a type signature.

The first example use something called guards. See this link for more info about guards: http://learnyouahaskell.com/syntax-in-functions#guards-guards

The second one uses pattern matching. (see the link above, scroll up to pattern matching)

These are, in this case, just two different ways of solving the same problem.

0

There is nothing good or bad about both the approaches. They are just a matter of preference. That being said, both functions should have the type signatures Int -> Int (even though they are optional in both the cases) because it improves readability and the type checker will complain if you do something wrong. So both of the code is effectively same: you are using guards in your first version and pattern matching in your second version.

Also, this question makes some good point of why type signatures are good for both of your functions.

Community
  • 1
  • 1
Sibi
  • 47,472
  • 16
  • 95
  • 163
0

if you ever want to find out more about those type signatures, it's very useful to use the :t tool in GHCI or your own program. If you are curious on why you are getting those errors that say type does not match expected type, you can always see what the function's type is. So in GHCI just type in :t take or :t repeat :t replicate ...you get the idea. Helps alot when you get into Lists and tuples...