
Jonathan Cast wrote:
On Mon, 2008-08-04 at 19:51 +0100, Brian Hulley wrote:
For example, why is there any distinction between a type "variable" and a type "constant"?
forall a b. (a -> b) -> List a -> List b
Now this begs the question: why does "List" need to start with a capital? (supposing that quantifiers were always written explicitly)
AFAIK, it doesn't; the variable/constant distinction is just there to let the implementation put the foralls in for you.
Hi Jonathan - thanks - this confirms what I've been thinking. However consider: class Foo a where f :: a -> b -> (a, b) Here there is no capitalization distinction in the type of (f) but the implementation can still insert the "forall b." since (a) is already in scope. Therefore similarly, if type constructors like "List" were instead written using lowercase, since they would already be in scope it would be clear even without explicit quantifiers that (a) and (b), but not (list), were to be quantified in the type of (map) (assuming of course that there was no top level data decl for (a) or (b)).
Similarly, if we had explicit foralls on equations, we could say
forall n x xn. drop (n+1) (x:xn) = drop n xn
but
forall n. drop n nil = nil
and it would be perfectly clear that `nil' in the latter case was a constructor.
Actually I quite like the above because it shows that pattern matching can be understood in terms of universally quantified predicates. Regarding the verbosity, patterns are something of a special case since variables (that are introduced in the pattern) can only occur once and there is no need to have higher rank quantification in value patterns (disregarding explicit tests for _|_). Therefore special syntax could be used to make the above shorter e.g. borrowing the '?' from Felix (http://felix.sourceforge.net/doc/tutorial/introduction/en_flx_tutorial_top.h... (section 2.11/2.17 etc)): drop (?n + 1) (_ : ?xn) = drop n xn drop ?n nil = nil (where glued prefix binds tighter than application).
Of course, if you want to introduce this rule, you'd better start out with it and be consistent --- require every variable to be explicitly brought into scope. I think you'd find it pretty annoying after trying to program in your new language for a while, though.
I agree that there are many good points in favour of Haskell's use of a case distinction, not the least being that it gives definite guidance on when to use lower or upper case identifiers and therefore avoids the mess you find in C++ programs where every programmer has their own private conventions which can lead to confusion and useless debate. However it still seems to me that the case distinction, at least on the level of type constructors, becomes problematic and possibly inconsistent especially if I consider the effects of trying to incorporate something like the ML module system as well as type classes. (For example the Moby language (http://moby.cs.uchicago.edu/ ), a possible successor to SML, uses capitalization for type constructors similar to Haskell which means that type parameters in functor applications must be uppercase, which conflicts with the use of lowercase type params for classes which are a special kind of functor... (Moby itself doesn't encounter any difficulty since it doesn't have type classes)). Best regards, Brian.