Yeah, the type inference is really going backwards, but I don’t think it is all the way from the output back to the input. The real critical part of the inference is the division operator: (/) :: Fractional a => a -> a -> a
. There you see that the input and output types to this operation must also be fractional. That information gets propagated backwards to the input and forwards to the output.
Your function is only useful on natural number inputs that are larger than 1, otherwise it will get stuck in an infinite loop because it will miss the harmonic 1
base case. So limiting the input to at least instances of type Integral
helps prevent some bad inputs. And I think it also makes sense that you can call this function with non-fractional types like Int
.
One disadvantage is that the fromIntegral
conversion between for example Int
and Double
is not completely free, so it will probably be slightly slower.
Another alternative is to make the base case stronger:
harmonic n
| n <= 1 = 1
| otherwise = harmonic (n - 1) + 1 / n
But this probably gives useless results on non-integer inputs too.
Also, I want to note that you can generalize the function by having the base case at n = 0
instead of n = 1
:
harmonic 0 = 0
harmonic n = harmonic (n - 1) + 1 / fromIntegral n
That makes it behave the same as an implementation using sum
:
harmonic n = sum [1 / fromIntegral m | m <- [1..n]]
Note that if n = 0
then the list [1..n]
is an empty list, so the result of the list comprehension is also an empty list and finally the sum of the empty list is 0.