Re: Existential Typing (was Multi-parameter OOP)
Leon Smith wrote: [snip] If GHC had true existential typing, as opposed to just existential datatypes, you could reasonably code what I think you want like this: class A a where basicA :: Bool nextA :: a - (EX a'. A a' = a') basicA = True nextA = id data WrappedA = forall a. A a = WrappedA a instance A WrappedA where basicA = False nextA (WrappedA a) = a data A1 = A1 instance A A1 --... similarly for B ... class AB a b where toBool :: a - b - Bool instance (A a, B b) = AB a b where toBool a b | (basicA :: a) (basicB :: b) = True | (basicA :: a) || (basicB :: b) = False | otherwise = toBool (nextA a) (nextB b) [snip] Either I've missed something, or this isn't what I wanted. The problem I have is that AB is not extensible. If you want to add a new instances A2,B2 of A,B and a new definition of toBool which just works for A2 and B2, you can't do it, without changing the definition of toBool in the above instance. ___ Glasgow-haskell-users mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Existential Typing (was Multi-parameter OOP)
On Friday 19 October 2001 11:02, George Russell wrote: Recently I've been experimenting with a sort of OOP with GHC, [...] I find your discussion rather intriguing, but I'm not sure I fully understand what you are trying to do. Existential typing allows for what I would call dynamic dispatch, which allows for the dynamic lookup of class members (i.e. methods). What you appear to be trying is something resembling dynamic typing. Dynamic typing can be emulated using dynamic dispatch. If GHC had true existential typing, as opposed to just existential datatypes, you could reasonably code what I think you want like this: class A a where basicA :: Bool nextA :: a - (EX a'. A a' = a') basicA = True nextA = id data WrappedA = forall a. A a = WrappedA a instance A WrappedA where basicA = False nextA (WrappedA a) = a data A1 = A1 instance A A1 --... similarly for B ... class AB a b where toBool :: a - b - Bool instance (A a, B b) = AB a b where toBool a b | (basicA :: a) (basicB :: b) = True | (basicA :: a) || (basicB :: b) = False | otherwise = toBool (nextA a) (nextB b) In this new setting, class AB seems a little silly. You could simply get rid of it. Of course, GHC doesn't work this way. Instead, you have to introduce a datatype StupidA to wrap your existential type in. For the benefit of this new stupid datatype, you'll also need to change the type of basicA from (:: Bool) to (:: a - Bool). This datatype also introduces unnecessary overhead, as you end up having chains of StupidA constructors that do essentially nothing. You could look at my attached code if you really want to. It has been beaten throughly with an ugly stick. From the purely denotational point of view of semantics, I love existential typing. I think this example really drives the point across that existential datatypes are not nearly as useful as existential typing. I can think of several similar situations in actual code of mine. However, using existential datatypes was overkill for the situation, and thus I opted for a different solution altogether. I don't understand all the implementation consequences of existential typing. Most importantly, how does existential typing effect the operational semantics? Mercury has existential typing, but then again, Mercury is newer and its design philosophy is far more ambitious. best, leon class A a where basicA :: a - Bool nextA :: a - StupidA basicA _ = True nextA a = StupidA a data StupidA = forall a . A a = StupidA a instance A StupidA where basicA (StupidA a) = basicA a nextA (StupidA a) = StupidA (nextA a) data WrappedA = forall a . A a = WrappedA a instance A WrappedA where basicA _ = False nextA (WrappedA a) = StupidA a data A1 = A1 instance A A1 class B b where basicB :: b - Bool nextB :: b - StupidB basicB _ = True nextB b = StupidB b data StupidB = forall b . B b = StupidB b instance B StupidB where basicB (StupidB b) = basicB b nextB (StupidB b) = StupidB (nextB b) data WrappedB = forall b . B b = WrappedB b instance B WrappedB where basicB _ = False nextB (WrappedB b) = StupidB b data B1 = B1 instance B B1 toBool :: (A a, B b) = a - b - Bool toBool a b | basicA a basicB b = True | basicA a || basicB b = False | otherwise= toBool (nextA a) (nextB b) main = return ()
Multi-parameter OOP
Recently I've been experimenting with a sort of OOP with GHC, using existential types and (overlapping, undecidable) multi-parameter type classes, but it doesn't seem to work as you might expect because of the way GHC resolves overloaded functions at compile-time. For example, given class A a data WrappedA = forall a . A a = WrappedA a data A1 = A1 instance A A1 class B b data WrappedB = forall b . B b = WrappedB b data B1 = B1 instance B B1 class AB a b where toBool :: a - b - Bool instance (A a,B b) = AB a b where toBool _ _ = False instance AB A1 B1 where toBool _ _ = True instance AB WrappedA WrappedB where toBool (WrappedA a) (WrappedB b) = toBool a b a naive user (like me a month ago) might expect that this to work, so that toBool (WrappedA a) (WrappedB b) will return False unless a is an A1, and b a B1, in which case it returns True. In fact ghc5.02 (rightly) gives an error message with the second instance declaration: Could not unambiguously deduce (AB a b) from the context (A a, B b) The choice of (overlapping) instance declaration depends on the instantiation of `a, b' So is there any other way of doing this sort of dynamic lookup at runtime, in a reasonably neat way? ___ Glasgow-haskell-users mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Multi-parameter OOP
Fri, 19 Oct 2001 17:02:54 +0200, George Russell [EMAIL PROTECTED] pisze: So is there any other way of doing this sort of dynamic lookup at runtime, in a reasonably neat way? There is module Dynamic. I don't know if it helps or is reasonably neat. -- __( Marcin Kowalczyk * [EMAIL PROTECTED] http://qrczak.ids.net.pl/ \__/ ^^ SYGNATURA ZASTÊPCZA QRCZAK ___ Glasgow-haskell-users mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Re: Multi-parameter OOP
At 2001-10-19 08:02, George Russell wrote: a naive user (like me a month ago) might expect that this to work, so that toBool (WrappedA a) (WrappedB b) will return False unless a is an A1, and b a B1, in which case it returns True. I think existential types are arranged so that Haskell never needs to store type information in them at run-time. So you'll never be able to do dynamic OOP with them. One possible extension to Haskell for dynamic OOP, which I never tire of suggesting, is the extensible datatype, for instance: module P data BaseType = B1 | B2 | _ module Q data DerivedType = D1 | D2 data BaseType |= BD DerivedType -- Ashley Yakeley, Seattle WA ___ Glasgow-haskell-users mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/glasgow-haskell-users