Re: Existential Typing (was Multi-parameter OOP)

2001-10-25 Thread George Russell

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)

2001-10-24 Thread Leon Smith

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

2001-10-19 Thread George Russell

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

2001-10-19 Thread Marcin 'Qrczak' Kowalczyk

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

2001-10-19 Thread Ashley Yakeley

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