Michael Hobbs writes:
> I have been "scratching a personal itch" lately and was wondering if
> anyone else has the same itch. [...]
>
> [...] it is not possible to create a list of values that may be of
> different [Haskell] type, but all implement the same interface.
>
> [...]
Hi.
Is it common OO programming practice to cast the elements of a
same-interface-different-implementation list, so as to recover the
original objects in full detail?
That's something I've idly wondered how to do in Haskell, but it isn't
a personal itch. The following code works with Hugs 98 (May 1999),
with the -98 flag.
> import Maybe(catMaybes) -- Only for the test at the end
>
> class MaybeBool a where
> treatAsBool :: a -> Maybe Bool
> treatAsBool _ = Nothing
> instance MaybeBool Bool -- A Bool is always a Bool (*)
> where treatAsBool = Just
> instance MaybeBool String -- A String is never a Bool
>
> class MaybeString a where
> treatAsString :: a -> Maybe String
> treatAsString _ = Nothing
> instance MaybeString String -- A String is always a String
> where treatAsString = Just
> instance MaybeString Bool -- A Bool is never a String
The next line of code would be a good place to put a preprocessor
directive. From it, and a naming convention, a preprocessor could
generate all the other code in this example, except for the test.
MyClass looks like an unnecessary piece of indirection, but it's meant
to be The Interface for the same-interface-different-implementation
list type [WrapMyClass]. In a non-toy program, it would have a
`where' block, and its `instance' definitions would be more than
boiler plate.
> class (MaybeBool a, MaybeString a) => MyClass a
> instance MyClass Bool
> instance MyClass String
>
> data WrapMyClass = forall a . (MyClass a) => WrapMyClass a
> instance MaybeBool WrapMyClass -- A WrapMyClass may hold just a Bool
> where treatAsBool (WrapMyClass x) = treatAsBool x
> instance MaybeString WrapMyClass -- A WrapMyClass may hold just a String
> where treatAsString (WrapMyClass x) = treatAsString x
>
> test =
> let sidiList = [WrapMyClass "So", WrapMyClass True]
> t1 = map treatAsBool sidiList
> t2 = map treatAsString sidiList
> in (t1, catMaybes t1, t2, catMaybes t2)
(*) The hypothetical preprocessor is up for a Banal Comment Award. ;-)
Regards,
Tom