
Michael Shulman wrote:
class TypeCast x y | x -> y, y -> x where typeCast :: x -> y
instance TypeCast x x where typeCast = id
Anyway, my main question about typeCast is this: why is it needed here at all?
First of all, there is a version of TypeCast that works within the same module, please see any code described in http://pobox.com/~oleg/ftp/Haskell/typecast.html Appendix D of the full HList paper (or, HList technical report, I should say) gives the reasons for TypeCast. Briefly, TypeCast lets us replace a (ground) type T with a type variable "t" subject to the constraint: "TypeCast t T => t". The difference seems superficial since the typechecker can immediately discharge the constraint and replace "t" with "T". Well, placing the TypeCast declaration in a different module, or the more convenient magic 6-line declaration of class TypeCast make sure that the typechecker delays the discharging of the "TypeCast" constraint. So the type variable "t" stays uninstantiated for a while. It matters in instance declarations. For example, class Foo x where foo :: x -> Int instance Foo [Int] instance Foo Bool where foo x = fromEnum x If we write test1 = foo [read "1"] we get an error: No instance for (Foo [a]). Indeed, `read "1"' has the type 'a', which does not match "Int". The typechecker certainly cannot chose the "Foo [Int]" instance because who knows what other instances of Foo may occur in other modules. We can tell the typechecker however that "Foo [Int]" is the only instance that can ever be in the whole program (that is, the world is `locally' closed, as far as the Foo [a] is concerned). We do that by writing instance TypeCast x Int => Foo [x] where foo [x] = typeCast x and now test1 = foo [read "1"] typechecks (and works), without any type annotations. BTW, if we lied and did write another instance for "Foo [T]" for some T, the typecheker will complain.