
Hello Arie, Tuesday, August 22, 2006, 7:24:34 PM, you wrote:
I disagree. As a new learner to Haskell, I already have a hard time keeping Constructors, Types, and Classes straight. I know what they all are and what they all do, but sometimes I really have to think hard to remember which is which in a piece of code. What helps my understanding is that each has a specific place in the type signature (which I guess includes 'nowhere' regarding constructors). Being able to put Classes where Types go would just serve to muddle that understanding.
(*) In this specific instance one might (ab?)use the additional notation to create a gentle introduction to type classes in a course/tutorial: one of the first lessons/chapters could state that the type of '(+)' is 'Num ->> Num -> Num', where 'Num' means "some numeric type" (stressing that it is *the same* type in all three places), only later confessing that this is actually shorthand for something more elaborate, and that the vague notion of "some numeric type" can be made explicit using type classes.
to be exact, it is intended usage - like in the OOP model, Num or [] can specify not only concrete type - it's something that can have subtypes. so, meaning of (+) :: Num -> Num -> Num or sequence :: [Monad a] -> Monad [a] or hTell :: SeekableStream -> IO Integral is simple and straightforward. And it's the _advanced_ material that identifiers used here may be not only defined by type declarations, but also by class declarations, and moreover - some of already studied type names denote classes actually. Subtyping introduced in very natural (at least for OOP souls) way. We may, for example, have functions: doit :: MemBuf -> IO Int hRequestBuf :: MemoryStream -> IO Int hTell :: SeekableStream -> IO Integral and call doit -> hRequestBuf -> hTell and then return result, and all will work fine because MemBuf is subclass of MemoryStream that is subclass of SeekableStream while Int is subclass of Integral. We can describe whole type hierarchy as having types at leafs and type classes at internal nodes As an example that clears my idea the following is function signatures from one my module: copyStream :: (BlockStream h1, BlockStream h2, Integral size) => h1 -> h2 -> size -> IO () copyToMemoryStream :: (BlockStream file, MemoryStream mem, Integral size) => file -> mem -> size -> IO () copyFromMemoryStream :: (MemoryStream mem, BlockStream file, Integral size) => mem -> file -> size -> IO () saveToFile :: (MemoryStream h) => h -> FilePath -> IO () readFromFile :: FilePath -> IO MemBuf As one can see, there is only one function that don't uses classes, and another one that can't be written using this syntax, another 3 is just created for using this proposal. I don't say that such ratio is typical, but at least i have a large number of polymorphic functions in my library and found the way to simplify most of their signatures: copyStream :: BlockStream* -> BlockStream** -> Integral -> IO () copyToMemoryStream :: BlockStream -> MemoryStream -> Integral -> IO () copyFromMemoryStream :: MemoryStream -> BlockStream -> Integral -> IO () saveToFile :: MemoryStream -> FilePath -> IO () readFromFile :: FilePath -> IO MemBuf i think that second block of signatures is an order of magnitude more readable -- Best regards, Bulat mailto:Bulat.Ziganshin@gmail.com