Building a hierarchy of GUI widget classes is pretty much a standard exercise in object-oriented programming. You have some sort of abstract Widget
class, with an a
The wxHaskell GUI library makes excellent use of phantom types to model a widget hierarchy.
The idea is the following: all widgets share the same implementation, namely they are foreign pointers to C++ objects. However, this doesn't mean that all widgets need to have the same type. Instead, we can build a hierarchy like this:
type Object a = ForeignPtr a
data CWindow a
data CControl a
data CButton a
type Window a = Object (CWindow a)
type Control a = Window (CControl a)
type Button a = Control (CButton a)
This way, a value of the type Control A
also matches the type Window b
, so you can use controls as windows, but not the other way round. As you can see, subtyping is implemented via a nested type parameter.
For more on this technique, see section 5 in Dan Leijen's paper on wxHaskell.
Note that this technique appears to be limited to the case where the actual representation of widgets is uniform, i.e. always the same. However, I am confident that with some thought, it can be extended to the case where widgets have different representations.
In particular, the observation is that object-orientation can be modeled by including the methods in the data type, like this
data CWindow a = CWindow
{ close :: IO ()
, ...
}
data CButton a = CButton
{ onClick :: (Mouse -> IO ()) -> IO ()
, ...
}
Subtyping may save some boilerplate here, but it's not required.