Fun with multi-parameter type classes

Hi, I've been getting into Haskell over the past few months and have just come up against a bit of a brick wall when trying to encapsulate some of the data structures in my code nicely. Basically what I want to have, is a type class where one of the intermediate values is opaque with respect to code using its implementations. This is a simplified version of what I'm trying to accomplish: class Foo t where encode :: String -> t decode :: t -> String instance Foo Int where encode = read decode = show test = decode . encode This currently fails, because the type checker insists on trying to figure out what its type should be - even though it shouldn't be needed. Any suggestions on how to encode this sort of thing? And if it is possible, can it be extended to multiple type parameters? as this is really what I want to use it for. About the only way I can think of "fixing" it, is by turning the code inside out - sort of like the way an AST drives the compiler, but without knowing how it represents things internally. Thanks, Sam

On Thu, Aug 19, 2004 at 05:42:10PM +0100, Sam Mason wrote:
Hi,
I've been getting into Haskell over the past few months and have just come up against a bit of a brick wall when trying to encapsulate some of the data structures in my code nicely. Basically what I want to have, is a type class where one of the intermediate values is opaque with respect to code using its implementations. This is a simplified version of what I'm trying to accomplish:
class Foo t where encode :: String -> t decode :: t -> String
instance Foo Int where encode = read decode = show
test = decode . encode
This currently fails, because the type checker insists on trying to figure out what its type should be - even though it shouldn't be needed.
You could "fix" it by type-annotating encode: test = decode . (encode :: String->Int) It makes sense that it doesn't work unannotated, since in the general case, you'd have more than one instance of Foo. Certainly you wouldn't want functions to stop working when you add one more instance. I think this sort of abstract manipulation is only possible with existential types. For example: data T = forall t.MkT (String -> t) (t -> String) x = MkT (read :: String->Int) show y = MkT (read :: String->Float) show test (MkT encode decode) = decode . encode --- *Main> test x "0" "0" *Main> test y "6.7" "6.7" -marius

On Thu, 19 Aug 2004, Sam Mason wrote:
class Foo t where encode :: String -> t decode :: t -> String
test = decode . encode
This currently fails, because the type checker insists on trying to figure out what its type should be - even though it shouldn't be needed.
In contrast to that, test = encode . decode should not fail. :-) Btw. for my association of the names 'encode' and 'decode' the signatures are the other way round, i.e.
decode :: String -> t encode :: t -> String

Henning Thielemann writes:
On Thu, 19 Aug 2004, Sam Mason wrote:
class Foo t where encode :: String -> t decode :: t -> String
test = decode . encode
This currently fails, because the type checker insists on trying to figure out what its type should be - even though it shouldn't be needed.
In contrast to that,
test = encode . decode
should not fail. :-)
Rright. The source file: bz = read . show is accepted. It even "works": Ok, modules loaded: Main. *Main> :t bz bz :: forall a a1. (Show a1, Read a) => a1 -> a *Main> bz 8 8 But try to use it for something more exquisite: *Main> bz 8.23 *** Exception: Prelude.read: no parse Of course a signature put by hand helps. But then what's the advantage of classes? Actually, I would like to know what was the purpose of all that... Jerzy Karczmarczuk

karczma wrote:
Actually, I would like to know what was the purpose of all that...
I was writing some new code and wanted to break it into two parts, they should have very little knowledge of each other other than what methods are available in each (hence the classes). The actual types of the values that were being passed around should have remained invisible to the other half of the code. Anyway, I didn't want to dump loads of code on you so I rewrote it in a simpler manner that took the form of the "read" and "show". The actual implementations weren't supposed to be of importance, just the fact that they were passing around something that the calling code shouldn't have known about. I've since realised that I can't solve the problem in the way I originally wanted to and have now sort of turned the code inside out so that there aren't any magically typed references floating around. Hope that makes a bit more sense now, Sam
participants (4)
-
Henning Thielemann
-
karczma
-
Marius Nita
-
Sam Mason