[Haskell-cafe] I'm stuck in my thought experiment
haskell at brecknell.org
Thu Aug 16 21:45:15 EDT 2007
> I was imagining a drag and drop web page designer. There are a bunch of
> Widgets (e.g., BlogWidget, TextWidget, MenuWidget, etc) that the user can
> place on the page.
> class Widget a where
> render :: a -> Html
> -- A page has a title and a Widget.
> -- I know this isn't valid Haskell, but I'm not sure how to specify what I
> -- want here. (existential types?)
> data Page = Page String Widget
If you wanted to make Page an existential type, it would look like this:
data Page = forall w . Widget w => Page String w
However, you will probably find it much more useful to define a single
existential wrapper for all widgets:
data WidgetW = forall w . Widget w => WidgetW w
Then, for example, you can construct polymorphic lists of widgets, as is
necessary for the column layout:
data Columns = Columns [WidgetW] -- hmm, needs spacing info.
And now Page just adds a string to a WidgetW:
data Page = Page String WidgetW
See the GHC users guide for more on how to use existential types.
> layout = Page "Main" (ColumnLayoutWidget [MenuWidget, ??? ])
> mainPage = ChildPage layout [TextWidget mainPageText]
> aboutPage = ChildPage layout [TextWidget aboutPageText]
Layout just looks like a function. Assuming an existing global menu
layout ws = WidgetW (Page "Main" (WidgetW (Columns (menu : ws))))
Note the type of layout: a function taking a list of widgets and
returning a single widget. You'll find a number of similar types useful
for structuring your code. For example, functions taking a single widget
and returning a single (more complex) widget, functions taking a single
widget and a list of functions each taking a widget and returning a
widget, and returning a list of widgets, and so on. It's called
functional programming for a reason. :-)
> This just gets trickier if I start considering multiple extension points
> for child pages and what happens when the layout/parent page changes.
> This is why I'm thinking I may be going along a bad path here.
Indeed, abstractions of this kind usually require a fair amount of
thought and experiment. I haven't given it either of these, but
hopefully the suggestions will help you become less stuck.
More information about the Haskell-Cafe