In article <Pine.LNX.4.21.0402150140100.16014-100000@dark.darkweb.com>, Ben Rudiak-Gould <benrg@dark.darkweb.com> wrote:
And it would be nice to be able to pass around values of type (exists t. Interface t => t), which behave just like OOP interface pointers.
A value of "type" (exists t. Interface t => t) consists of two values, one of type t, and one "dictionary" value. For that reason a data type is used to represent this (and a newtype type cannot be). So what's the difference? Data provides another layer of "thunkage". For instance: data D = MkD Int; newtype N = MkN Int; Then (MkN undefined) is the same as undefined, but (MkD undefined) is not. So how does this apply to (exists t. Interface t => t)? Well, you'd have two different versions of "undefined" depending on whether calculation of the dictionary was part of the undefinition.
I don't see how
openInputStream :: FilePath -> IO (exists t. InputStream t => t)
Bear in mind you can't even write IO (forall t. whatever) in Haskell.
would cause any more problems than
data Foo = forall t. InputStream t => Foo t openInputStream :: FilePath -> IO Foo
What am I missing?
Simply that undefined is not the same as (Foo undefined). Quite separately, if InputStream happens to look like this: class InputStream t where { f1 :: t -> something; f2 :: t -> something'; f3 :: t -> something''; -- etc. }; where none of the somethings refer to t, you'd be better off with a data-type: data InputStream { f1 :: something; f2 :: something'; f3 :: something''; -- etc. }; This is a much better way of doing semi-OOP. AFAIK you can't really do proper OOP-style extensibility in Haskell at all (and "exists" wouldn't help either). -- Ashley Yakeley, Seattle WA