
You don't need meta-programming technology (reflection) to do things like generic prinitng. A generic programming extension of Haskell (like Generic Haskell, or derivable classes) can do the job for you.
Isn't generic programming usually based on a kind of compile-time reflection (if the argument is of this type, do this, else..)?
Yes, you might view it as compile-time reflection. Programs are still typable.
And don't you write generic functions with the intention that the implementation will figure out the approriate type-specific variants, i.e., you write your code at a level of abstraction that is not easily reached with the standard language tools -- a meta level, where functions at different types are the first-class objects of discourse?
If you view that as a metalevel I agree. However, it is the intention you never see the generated functions at different types: the compiler can (in priciple) completely hide those from the programmer. In that sense there is just one level: Generic haskell offers you normal Haskell functions and generic functions.
I find it helpful to think of generic programming support as one way of integrating well-behaved subsets of reflection and other meta-programming techniques into Haskell.
It is partly a trade-off: you get some of the features and avoid some of the problems of a fully reflective architecture. It is also a specialisation: by avoiding the full generality, the specific subset of features can be designed in a structured fashion, with the application domain in mind, making them easier to use for that domain.
Agreed. -- Johan