
I have a typeclass related question that I have been puzzling over. In a library I am working on, I have a series of functions for converting values to Renderables: | labelToRenderable :: Label -> Renderable | legendToRenderable :: Legend -> Renderable | axisToRenderable :: Axis v -> Renderable | layoutToRenderable :: Layout x y -> Renderable These names are overloaded for convenience via a typeclass: | class ToRenderable a where | toRenderable :: a -> Renderable | | instance ToRenderable Label where | toRenderable = labelToRenderable | ... But some recent changes mean that Renderable needs to become a type constructor, and the functions now product different types: | labelToRenderable :: Label -> Renderable () | legendToRenderable :: Legend -> Renderable String | axisToRenderable :: Axis v -> Renderable () | layoutToRenderable :: Layout x y a -> Renderable a Is there a nice way to overload a "toRenderable" function for these? Something like this is possible: | class ToRenderable a b where | toRenderable :: a -> Renderable b But the above is, I think, too general for my needs. I don't want to be able to generate Renderables of different type b for a single input type a. Also, MPTC take me out of the world of haskell 98, which I was trying to avoid. Am I missing something simple? Any pointers would be much appreciated. Tim

| class ToRenderable a b where | toRenderable :: a -> Renderable b
But the above is, I think, too general for my needs. I don't want to be able to generate Renderables of different type b for a single input type a.
Sounds like a functional dependency (class ToReadable a b | a -> b )
Also, MPTC take me out of the world of haskell 98, which I was trying to avoid.
Why. "Everyone does it", and MPTC will be in Haskell-Prime (but FD may be not) http://hackage.haskell.org/trac/haskell-prime/wiki/MultiParamTypeClassesDile... J.W.

Also, MPTC take me out of the world of haskell 98, which I was trying to avoid.
Why. "Everyone does it",
Well, it's a library that others might use, so I would prefer to avoid using language extensions, especially functional deps which I don't understand, and which seem to have an uncertain future. Tim

Well, it's a library that others might use, so I would prefer to avoid using language extensions, especially functional deps which I don't understand, and which seem to have an uncertain future.
I think there will be a storm of protest if support for this simple shape of dependencies ( ... | a -> b ) would be dropped from the major Haskell implementations. (There used to be some status page on what compiler supports what extension, anyone know the current location for that?) J.W.

On Thu, 2008-09-11 at 13:23 +0200, Johannes Waldmann wrote:
Well, it's a library that others might use, so I would prefer to avoid using language extensions, especially functional deps which I don't understand, and which seem to have an uncertain future.
I think there will be a storm of protest if support for this simple shape of dependencies ( ... | a -> b ) would be dropped from the major Haskell implementations.
For backwards-compatibility reasons, or because you think they're better than type families? Personally, I am quite enthusiastic about type families, although that is influenced by a (somewhat abandoned) project of mine that ended up with a 3 parameter type class (5 for the sub-class created for quickCheck support) with one-to-one relations every way. And multiple `global' variables implemented with dynamic parameters (they would have needed to be thread-local, eventually, anyway) with types parameterized on the afore-mentioned 3 parameters plus two more to allow the choice between ST and STM. When you get types like this: -- | Wait for another thread to change the buffer contents. displayWaitRedisplay :: (Buffer b d mk, ?currentBuffer :: BufferState b d mk STM TVar, ?currentWindow :: Window b d mk c STM TVar) => b TVar -> STM () types like this: -- | Wait for another thread to change the buffer contents. displayWaitRedisplay :: (Buffer b, ?currentBuffer :: BufferState b STM, ?currentWindow :: Window b c STM) => b TVar -> STM () look like heaven. jcc

if support for this simple shape of dependencies ( ... | a -> b ) ...
For backwards-compatibility reasons,
Yes.
or because you think they're better than type families?
Don't know (haven't used them). Concrete example: I have this "class Partial p i b | p i -> b" http://dfa.imn.htwk-leipzig.de/cgi-bin/cvsweb/tool/src/Challenger/Partial.hs... What would type families buy me here? In my code, this class has tons of instances (I count 80). How much would I need to change them? Could this be automated? Thanks - J.W.

On Thu, 2008-09-11 at 18:34 +0200, Johannes Waldmann wrote:
if support for this simple shape of dependencies ( ... | a -> b ) ...
For backwards-compatibility reasons,
Yes.
This gives point, then, to my concerns about letting Haskell become a practical language. At some point, production systems always seem to be end-of-lifed by backwards compatibility.
or because you think they're better than type families?
Don't know (haven't used them).
Concrete example: I have this "class Partial p i b | p i -> b" http://dfa.imn.htwk-leipzig.de/cgi-bin/cvsweb/tool/src/Challenger/Partial.hs...
What would type families buy me here?
I can't figure out what b is. I could, of course, argue that it would force you to come up with a name for `b', so people reading the code could understand what it does.
In my code, this class has tons of instances (I count 80). How much would I need to change them?
instance Partial p i b where => instance Partial p i type B p i = b And type signatures involving Partial would have to change.
Could this be automated?
To a certain extent. Finding the places that need to change could be automated, which is always the first step :) jcc

Hi Tim,
Your example seems like a perfect fit for functional dependencies.
On Thu, Sep 11, 2008 at 3:36 AM, Tim Docker
Well, it's a library that others might use, so I would prefer to avoid using language extensions, especially functional deps which I don't understand, and which seem to have an uncertain future.
I completely agree with you that it is a good idea to stick to Haskell'98 when you can, especially in library code, so you'll have to decide if you really want to use the class. As for not understanding functional dependencies, it sounds like you are not giving yourself enough credit. Your previous comment basically contains the definition of a functional dependency: | But the above is, I think, too general for my needs. I don't want | to be able to generate Renderables of different type b for a single input | type a. This is all there is to a fun. dep., from a programmer's perspective---it adds a constraint on the instances one can declare for a given multi-parameter type class. Hope this helps, -Iavor

On Thu, 11 Sep 2008, Tim Docker wrote:
I have a typeclass related question that I have been puzzling over.
In a library I am working on, I have a series of functions for converting values to Renderables:
| labelToRenderable :: Label -> Renderable | legendToRenderable :: Legend -> Renderable | axisToRenderable :: Axis v -> Renderable | layoutToRenderable :: Layout x y -> Renderable
These names are overloaded for convenience via a typeclass:
I think that type classes are not for keystroke reduction, but for writing generic algorithms. If there is no algorithm that becomes more generic by the use of a type class, I would not use a type class, but stick to labelToRenderable and friends.

(Henning:)
If there is no algorithm that becomes more generic by the use of a type class, I would not use a type class, but stick to labelToRenderable [...]
The problem with function names as "labelToRenderable" is that they have type information as part of the name. Consistency of that information cannot be enforced by the language, which makes it dangerous. If you want type information (e.g. to resolve overloading, for the compiler - and for the reader!) use the language, and write a type annotation. J.W.
participants (5)
-
Henning Thielemann
-
Iavor Diatchki
-
Johannes Waldmann
-
Jonathan Cast
-
Tim Docker