Suppose you want to define a function that calculates the square of the hypotenuse of a right triangle. Any of the following definitions are valid
hyp1 ab = a * a + b * b hyp2(a,b) = a * a + b * b
However, they are not the same function! You can tell by looking at their types in GHCI
>> :type hyp1 hyp1 :: Num a => a -> a -> a >> :type hyp2 hyp2 :: Num a => (a, a) -> a
Taking hyp2 first (and ignoring the Num a => ), the type tells you that the function takes a pair (a, a) and returns another a (for example, it can take a pair of integers and return another integer or a pair of real numbers and return other real number). You use it like this:
>> hyp2 (3,4) 25
Please note that parentheses are not optional here! They guarantee that the argument is of the correct type, a pair a s. If you do not enable them, you will receive an error message (which will probably look very confusing to you now, but be sure that it will make sense when you learn about class types).
Now, looking at hyp1 , one way to read type a -> a -> a is two things of type a and returns something else of type a . You use it like this:
>> hyp1 3 4 25
Now you will get an error if you include parentheses!
So, the first thing to notice is that the way you use the function should match the way you defined it. If you define a function using parens, you must use parens every time you call it. If you do not use parens when defining a function, you cannot use them when you call it.
Thus, it seems that there is no reason to prefer one over the other - it is just a matter of taste. But in fact, I think there is a good reason to prefer one over the other, and you should prefer a style without parentheses. There are three good reasons:
It looks cleaner and makes it easier to read code if you don't have partners cluttering the page.
If you use parsers everywhere, you will get a performance hit because you need to create and deconstruct a pair every time you use this function (although the compiler can optimize this - I'm not sure).
You want the benefits of currying, aka partially applied functions *.
The last point is a little thin. Recall that I said that one way to understand a function like a -> a -> a is that it takes two things of type a and returns another a . But there is another way to read this type, which is a -> (a -> a) . This means exactly the same thing, since the -> operator is right-associative in Haskell. The interpretation is that a function takes one a and returns a function of type a -> a . This allows you to simply provide the first argument to the function and apply the second argument later, for example
>> let f = hyp1 3 >> f 4 25
This is practically useful in a variety of situations. For example, map functions allow you to apply some function to each element of the list -
>> :type map map :: (a -> b) -> [a] -> [b]
Say you have a function (++ "!") That adds a hit to any String . But you have Strings lists, and you want them all to end in a bang. No problems! You just partially use the map function
>> let bang = map (++ "!")
Now bang is a function of type **
>> :type bang bang :: [String] -> [String]
and you can use it as follows
>> bang ["Ready", "Set", "Go"] ["Ready!", "Set!", "Go!"]
Pretty helpful!
I hope I convinced you that the convention used in your training material has some pretty good reasons to use. As someone with a mathematical background myself, I see the appeal of using a more โtraditionalโ syntax, but I hope that as you progress along your programming journey, you can see the benefits of changing what is initially unfamiliar to you.
* Note for pedants - I know that currying and partial application are not exactly the same thing.
** In fact, GHCI will tell you that the type is bang :: [[Char]] -> [[Char]] , but since String is synonymous with [Char] , it means the same thing.