I don't think it is. Explicitly listing types is definitely overrated though. Basically the same function in haskell:
pairwise (a:b:xs) f = (f a b):(pairwise (b:xs) f)
pairwise _ _ = []
What does ghci know about it now?
pairwise :: [t] -> (t -> t -> a) -> [a]
Without any user annotation - magic! :) Just in case - mapping to the original types from the article:
TResult == a
IEnumerable<TResult> == [a]
TSource == t
Func<TSource, TSource, TResult> == t -> t -> a
Shorter notation and a bit of type inference can make that example a lot easier. And it is completely type-safe which can save you in case the predicate tries to do something stupid.
I don't know anything about Haskell than a bit of its history and main philosophy, but if I understood that example right then basically you say how things can combine and if you combine them in 'wrong' ways it gets caught without having to name every possible instance of such combinations ?
I'm not sure this answer is what you're looking for, but:
The function does the same thing as the Pairwise in the article. What haskell does is "guess" at what the types of each variable / function can be, which is fairly easy in this case. For example pairwise expects a list in the first argument (because it's split into "a","b" and the rest of items), and some other argument "f". "f" is called with "a" and "b" as arguments (which are of the same type, because they come from the same list) and returns some result. The return value of "pairwise" is constructed from either results of "f" or an empty list, so the result must be a list of the items that "f" returns. etc.
So the compiler itself can figure out all the types needed in that function without any help from the user. (basically we could do it in the C# case too, even if someone removed all the types) Haskell will happily compile that function with the signature it found out and will let you run "pairwise [1,2,3,4] (\x y -> x-y)". But it will not even compile your program if you try to use a number as the first parameter or a function with different signature in the second. It's just a function with generic arguments and some limits on what you can use ("f" has to take both arguments of the same type - you can't use "Char -> Int -> a" there).
You're still allowed to run "pairwise "abc" (\x y -> x:[y])" and will get "ab" "bc" - however there's no dynamic duck typing involved.
So my point was that there's nothing bad about the static typing itself. In most cases you don't need to write all the types yourself - they can be easily guessed and it's much better if the compiler does it for you.
Right, that's what type inference is all about. Impressive they can take it that far without having any 'input' from the programmer, that's something that should make its way into other languages.
Hindley–Milner type inference is a mathematically-sound type inference system. Given certain not-entirely-unreasonable constraints, it is always correct. Given a program written entirely in those constraints, HM will be fully correct, no matter how large you make it.
Unfortunately, the constraints are indeed a little limiting. Haskell offers many advanced features that technically break HM type inference. Thus, in Haskell it is considered good practice to always label your functions with their type signatures. (Human-made ones are often better; the compiler may have been more generic than you actually want (though still correct), or you may be able to better "spell" the type signature using your type synonyms that mean something to humans.) Then, even if there is a function that manages to confuse the type inferencer, the damage is contained.
It's fairly easy to do the type annotation, too; nobody has to know that what you did was leave it off, load it into the compiler, and ask the interpreter what the inferred type was. :) I'm sure Haskell experts don't ever have to do that, but I'm still learning and there's definitely been some times where I could write a function that worked as I expected but I couldn't quite follow the type. So far I'm not confusing the type inferencer very often; mostly it just manifests as types I'd "spell" differently but are equivalent.
Thus, in Haskell it is considered good practice to always label your functions with their type signatures.
Haskell is 20 years old, and this has been made 'good practice' only very recently -- I'd date it to dons's revival blitz a few years ago. A professor of mine who'd been using Haskell from the beginning was annoyed by their use.
that's something that should make its way into other languages.
How do you think javac knows when you get the types wrong? It's inferring types internally, it just makes you repeat yourself out of fealty to Bondage and Discipline.
"that's something that should make its way into other languages"
It's starting to, to some extent. C#, for example, has had inferred types available since 3.0, though it still requires using the "var" keyword when declaring variables.
I don't think it is. Explicitly listing types is definitely overrated though. Basically the same function in haskell:
What does ghci know about it now? Without any user annotation - magic! :) Just in case - mapping to the original types from the article: Shorter notation and a bit of type inference can make that example a lot easier. And it is completely type-safe which can save you in case the predicate tries to do something stupid.