Nah. ScopedTypeVariables is extremely commonly used, and Haskell as a language is one where frequent use of extensions is common generally. Saying “you can’t even add type annotations to let bindings” shows a misunderstanding of Haskell, because you
can and the way the language accomplishes it is with ScopedTypeVariables. It’s not “special” or “extra” in Haskell, using extension pragmas is just a super common, mundane, every-day thing in Haskell.
I should also point out that when you say
> “They're only available in type & function definitions.”
you are wrong.
Firstly, you can (and should almost always) use type annotations for plain values, like
pi :: Float
pi = 3.14
and e.g. with more complex data constructors like in < https://en.m.wikibooks.org/wiki/Haskell/Type_declarations > section on constructor functions.
For value constructors that use complex type dynamism in Haskell, like the return value polymorphism in Haskell regular expressions, it’s critical to use type annotations on plain values, because the type annotation actually resolves which of the polymorphic return types will be chosen.
Secondly, and more importantly, you can use type annotations on bindings natively in a where clause, and this is a very fundamentally important thing to do, especially for locally defined helper functions.
Then you only need ScopedTypeVariables if your helper function needs to involve a generic type constraint from the polymorphism of the outer function declaration, which is vastly more rare.
I’d also say the comparison with the types of local variables that tend to be unannotated in Haskell let or where clauses is very disingenuous and it’s actually not similar to the iterator boilerplate example you gave before at all.
When all the arguments have well-annotated types, and you use a let or where variable to hold a temporary value, the type is inferred by a reader much more easily than when using auto in C++, and has a direct meaning in terms of the calculation at hand (e.g. it’s not done to reduce boilerplate like auto).
For example, if you’re trying to calculate distance between two tuples (x0, y0) and (x1, y1), where the types of those tuple elements are given explicitly just a few lines up, then there is little harm in doing something like
where dx = x1 - x0
though it’s still preferable to say e.g.
where dx = x1 - x0 :: Float
This is emphatically not what you’re doing with auto, because the types are not otherwise locally obvious, and even if they were, a judicious use of typedef / typename saves just as much syntax while being far more descriptive.