This is an example of currying and partial application.
Note from the first link:
In Haskell, all functions are considered curried: That is, all functions in Haskell take just one argument.
This probably sound strange, but it makes sense when you remember that Haskell has first class higher-order functions. Instead of having a function that takes multiple arguments, a curried function takes one argument and returns a function that waits for the rest of its arguments. So in the case of your code,
createAddFunc :: Int -> (Int -> Int)
is a function that takes one integer as an argument and returns a function that takes one integer as an argument and returns an integer. Or more clearly, it is a function that takes one integer as an argument and returns (a function that takes one integer as an argument and returns an integer). When you have a function in Haskell that looks like it takes more than one argument, meaning it has more than one variable name on the left hand side of the =, that means the function is curried. It will take one argument and then return a function waiting for the rest of its arguments until it has them all and then returns the non function return value.
This is very useful because it means we can easily do partial function application, which is what you did with adds5. adds5 is exactly what createAddFunc should return when given one Int argument: a function waiting for an Int which when given one will return an Int.
So, no, Haskell isn’t doing any magic and deciding on its own whether or not you meant to return a function or to return some other value.
The reason why
Int -> (Int -> Int)
--and
Int -> Int -> Int
are the same in this case is because, if you think through the process of currying single value arguments, the parentheses will associate automatically from the right.
foo :: Int -> Int -> Int -> Int -> Int -> Int
Is a function which takes an Int and returns a function which takes an Int which returns a function that takes an Int which… Note that it takes an Int first and returns a function, so we could have written
foo :: Int -> ( Int -> Int -> Int -> Int -> Int)
so what would the type of foo 1 be?
foo 1 :: Int -> Int -> Int -> Int -> Int
which of course by the same logic we could have written
foo 1 :: Int -> (Int -> Int -> Int -> Int)
and I’m sure you can follow through with the rest of the reductions that happen when we add more arguments. When you get to the end, you find that the original function could have been written
foo :: Int -> (Int -> (Int -> (Int -> (Int -> Int))))
But that would be way too much to write every time, so Haskell automatically associates functions from the right, leading to us being able to write function definitions without needing to place all of the parentheses.
However, note that parentheses do matter in general. You can’t just add them in randomly, because it will change some values from being things like Ints to being functions themselves and then the compiler will complain that you gave it an Int instead of a function. The times when you don’t need to write the parentheses are exactly when everything is right associative. For example consider a function which takes an Int, a function taking an Int and returning an Int, and then applies that function to the first argument:
bar :: Int -> (Int -> Int) -> Int
bar x f = f x
bar 3 (+3)
6
If you tried to write that function without parentheses
bar' :: Int -> Int -> Int -> Int
bar' x f = f x
you would get a compiler error for a few reasons, one of which being that the body doesn’t reflect what the type says, which is shows that bar and bar’ have different types. You can see why by putting in our associative parentheses:
bar :: Int -> ((Int -> Int) -> Int)
bar' :: Int -> (Int -> (Int -> Int))
and those are clearly not the same function. In our attempt to define the body of bar’, we would be applying an Int to an Int and expecting it to return to us a function waiting for an Int to then return us another Int. And the compiler is not happy with that.
So, parentheses are not just white noise, they matter a lot. They can change the entire meaning of a function if you group them differently in most cases. However, because Haskell is curried by default, all multi parameter functions are really higher order functions, and the types automatically associate from the right. You can always leave those parentheses out, but those are the only ones you can leave out.