A Java desktop application exposes the class/containment hierarchy of its Swing GUI through RPC. I'm building a Haskell client that connects to the application. The objective is to be able to perform queries like "find all the buttons of width less than w, located within screen rectangle r" over a data structure on the Haskell side.
I'm not sure about how to best represent the structure, especially regarding inheritance/subtyping. I have tried two approaches. The first one:
data Component = Component
{
componentId::Int,
pos::(Int,Int),
dim::(Int,Int),
componentType::ComponentType,
children::[Component]
}
data ComponentType =
ButtonComponent Button
-- other component types here
|OtherComponent
data Button = Button
{
buttonText::Text
-- other button attributes here
}
I'm also considering a second solution using existential types, together with Typeable to implement "downcasting":
data Component = Component
{
componentId::Int,
pos::(Int,Int),
dim::(Int,Int),
children::[AnyComponent]
} deriving Typeable
data AnyComponent = forall a . ( Typeable a, HasComponent a ) => AnyComponent a
class HasComponent a where
base :: a -> Component
downcast :: Typeable b => a -> Maybe b
instance HasComponent Component where
base = id
downcast = cast -- cast function is from Data.Typeable
instance HasComponent AnyComponent where
base (AnyComponent a) = base a
downcast (AnyComponent a) = cast a
data Button = Button
{
buttonComponent:: Component, -- pointer to "superclass" info
buttonText::Text
-- other button attributes here
} deriving Typeable
instance HasComponent Button where
base = buttonComponent
downcast = cast
What solution is more idiomatic Haskell? Are there better solutions?
1 Answer 1
EDIT: Faithfully representing OO semantics in Haskell is currently impossible. In particular, method resolution can be simulated (but not easily, and not quite faithfully) via type classes and instances. This paper is very informative about current best practice, and possible future evolution.
Here is one possibility, which is similar to your first solution:
data ComponentClass a
= ComponentClass {
...
, comSubclass :: a
...
}
type Component = ComponentClass ()
data ButtonClass a
= ButtonClass {
, buttonText :: Text
, btSubclass :: a
...
}
type Button = ComponentClass (ButtonClass ())
This is what I would do if I thought a simple hierachy fit my problem domain well (but not necessarily to mirror the OO in another language). However, this strategy does not work well if you need to track deep hierarchies, or multiple superclasses (such as in Python).
Another disadvantage is that you cannot deconstruct an arbitrary
ComponentClass a
into its subtype a
. You can only do this if you
know the type of a
in advance. However, you could use dynamic typing
to fix this issue, such as via Typable
.
Note that you could also represent the hierachy in the up direction as well:
data ButtonClass a
= ButtonClass {
btText :: Text
, btSuperClass :: a
}
type Button = ButtonClass ComponentClass
You could then build each object "down the class hierarchy" until you discovered the most concrete type to wrap it up in.
Also note that Gtk2Hs uses a typeclassing strategy similar to your second one to represent the class hierachy of Gtk. Perhaps a look at its source code would be edicational, or even just browsing its documentation on Hackage.
Finally, the Exception
hierachy implemented in GHC
is quite interesting, though I don't think it would help you as much as either of the above---it's quite flat by comparison.
Data.Dynamic
, which builds atopData.Typeable
. \$\endgroup\$