Thursday, October 17, 2013

A cascade menu coded in pure applicative & monadic haskell?

Now the mflow demo has a cascade menu.
The menu works as any cascade menu, but it does not use explicit javascript to display and hide the branches when clicked. It uses pure haskell with applicative and monadic combinators.

It uses the behaviour of the monad instance of the  MFlow  formlets. But in this case it uses links instead of forms elements.

Here is the code of two branches of the cascade menu:

absLink ref = wcached (show ref) 0 . wlink ref

mainMenu ∷  View Html IO Options
mainMenu= autoRefresh $
  ul <<< li <<<  do
          absLink DatabaseSamples << b  "Database examples"
             <++ " with different backends"
          ul <<<
           (li <<< (absLink MFlowPersist <<  b "Persistent")  <! noAutoRefresh
                     <++ do -- blaze-html monad
                        b " illustrates the use of MFlow with "
                        a  "Persistent" ! href yesodweb
                        " (In this example sqlite backend is used) "
                        article persistentarticle

           <|> li <<< (absLink Database << b  "Database") <! noAutoRefresh
                     <++ b " Create, Store and retrieve lines of text from Amazon SimpleDB \
                            λ storage "
                     <> article amazonarticle)
   <|> li <<<  do
          absLink PushSamples << b  "Push Samples"
             <++ " using long polling"
          ul <<<
           (li <<< (absLink Push << b  "Push example") <! noAutoRefresh
                     <++ b " A push widget in append mode receives input from \
                             λa text box with autorefresh"
                     <> article pushl

           <|>   li <<< (absLink PushDec << b  "A push counter") <! noAutoRefresh
                     <++ b " Show a countdown. Then goes to the main menu"
                     <> article pushdec)
   <|> li <<< do

It works as follows:

The menu is a set of links composed by applicative alternative (<|>) operators. each operator contains a do sequence  so according with the monad instance, the sequence executes as long as the previous lines of the sequence are validated by the user input, so when the first link in the do sequence is clicked, the next sentence can be executed. So when the top level link is clicked, what appears is a second level of links composed again with the (<|>) operator. They are the terminal branches in this case. (the process can go down further with a new level, if it is necessary). The final branches are absolute links with the class= _noAutoRefresh attribute. This tell autoRefresh to escape from local auto-refreshing and permit a page navigation when this link is pressed.

autoRefresh install the javascript and server flags necessary for auto-refreshing the menu without refreshing the whole page.

The operator (<<<) encloses the HTML of a widget within anoter HTML tag. In this case the tags UL and LI encloses the links.

absLink is a cached wlink and thus has a fixed path. this absolute link is convenient since the code backtrack when a menu item is cliked from any option (see this article).

 (<++) append HTML code to a widget.

The behaviour can be improved, for example,if  instead of collapsing each branch when a new branch is open, to maintain any number of branches open,  just add its own autoRefresh for each branch instead of a general autoRefresh.

One additional NOTE about this code: There is a round-trip to the server each time an option is pressed, but for once, this permits the presentation of mutable data which is arranged in three-like structures, not only static menus. In the other side, the server code that respond the requests is ready waiting and with all the context in place, no lookups, typical of MVC frameworks are necessary, so the response has a very short latency, about 5 ms latency in the server, according with my heroku box.  Here are the typical response parameters for the menu refresh:

 connect=1ms service=5ms status=200 bytes=2862

No comments: