Sunday, December 30, 2012

On the "spirit" of MFlow. Anatomy of a Widget


TCache, RefSerialize, Workflow and MFlow are packages created in the process of building a system for massive workflows in the Web. It could be used for electronic democracy among other applications. MFlow  has been designed to program Web applications at a high level that I need by sacrificyng stability, completeness and some simplicity. It is also based of the idea of self contained code, composability and to encapsulate as much "magic" as I can.

Haskell can do a lot of magic in MFlow: It can express a web navigation flow in a way that in Java would need a configuration file. it can run a web server procedure backward to respond to a previous page in case the browser back button was pressed.  It can free resources by killing a process on timeout, because it can restart the server process when necessary and recover the computation state. It can get rid of database storage  and data storage definitions because all is re-created from simple events. It can compose statically typed pages including links and scripts. All these are unique characteristics that are unique in MFlow, but also it is possible a classical stateless application, with data storage defined by the user in a database.

Since the workflow concept need a way to checkpoint events,  The Workflow package has a monad that store the internal and external events, and restore its state from them without the need for an additional snapshoot of the  current state, which  indeed would need an additional data definition beside the definition of the events. That is what is called "event sourcing".

MFlow uses the hability of Workflow to store and rebuild the state of the data from the events, in this case, produced by user interactions in the Web. This means that there has no need for a data layer if you don´t want it. Just the automatically generated log of events is enough. This advantage becomes a necessity when a data definition of the current state is not only redundant but impossible without an additional interpreted DSL. For example I want to use MFlow for a functionality in which the user define processes by means of a wizard-like interaction, with some questions asked to the user. The generated description is a program that has no direct data serialization except by means of a interpreted DSL that would be painfully complicated, and would not add any additional functionality except the intended persistence of the current state. Instead,  this programming overhead can be avoided by using event sourcing. See here.

RefSerialize is a serialization library that reduces the size of the event log by referencing multiple repeated events to a single description in the log file. TCache gives transactionality and read/write caching, so that log writing and reading is done in bursts according with the cache write policy, and in coherence with other kind of non-event data that may be used by the application.

As I said before, the requisite of self contained-ness for the creation of components is very important for me. It means that the habitual mess of configuration files, heterogeneous dscriptions, templatings, scripts, all of them in different files that make necessary a deployment step and a complete manual for reusing it is avoided. You can still have your separate  header-footer thenplate in a different file, codified using XML,  but this is not imposed. You can have all of this in the same file.

Here below is an example of a reusable widget in MFlow. A widget is an active component. It ever return something statically typed, verified at compilation time. The output may be the result of a click in a link, a form submit or a script. It can have Ajax interactions.  It can use/contain other widgets or combinations of widgets that interact with the server server trough ajax.It uses Ajax to call 'prependWidget', that insert a new empty widget of type 'a' on top of the list. The widget 'requires' the installation of  a short 'ajaxScript' that is inline, and the jquery file that is referenced by the URL. When jquery is loaded, a online script,  'installevents' is called and install the ajax event.




wEditList :: (Typeable a,Read a
             ,FormInput view
             ,Functor m,MonadIO m, Executable m)
          => (view ->view)
          -> (Maybe String -> View view Identity a)
          -> [String] -> View view m  [a]
wEditList holderview w xs = do

    let ws=  map (w . Just) xs
        wn=  w Nothing
    id1<- genNewId
    let sel= "$('#"<>  B.pack id1 <> "')"
    callAjax <- ajax . const $ prependWidget sel wn
    let installevents= "$(document).ready(function(){\
              \$('#wEditListAdd').click(function(){"++callAjax "''"++"});})"

    requires [JScript ajaxScript, JScriptFile jqueryScript [installevents] ]

    ws' <- getEdited sel

    r <-  (holderview  <<< (manyOf $ ws' ++ map changeMonad ws)) <! [("id",id1)]

    delEdited sel ws'

    return  r



'prependWidget' stores the added widgets in the edited list, that is accessed with 'getEdited'.  This last is a shorhand for 'getSessionData'', which stores and retrieves user-defined data in the session, in this case the inserted widgets, by means of a map indexed by data type. In this way the user don´t need to pass its application data by parameters, neither it need to create its own state monad. 'getEdited' just uses its own type for storing widgets.

Finally, the value returned by the widget  is 'manyOf' the widgets added "ws'" and the initial widgets, "ws".  'manyOf' return only the validated widgets of the list. 'holderview' is the tag that encloses the list of edited widgets. the operator  '<<<'  add an encloser tag to the widget, and the operator '<!' gives atributes to the topmost tag of the widget, so the tag 'holderview' get assigned the  tag identifier 'id1'.  id1 is a name automatically generated. This is necessary for 'prependWidget' to add a new widget to 'id1' because his first parameter, the selector 'sel'  points to 'id1'.

Because the edited widgets are internally cached with 'wcached'  they must be in the Identity monad. to switch to the 'm' monad, 'changeMonad' is used. In the future, probably I will use IO only and disallow the use an arbitrary monad, since the user can store in session arbitrary data thanks to 'setSessionData' so there is no need for an arbitrary monad. That would make the types in MFlow less intimidating for beginners, and reflect the effort on simplicity without sacrificing power.

Finally the Edited list is deleted.before returning the result.

This widget is used in "demos/demos-blaze.hs" and is part of the MFlow release. I´m programming reusable active widgets general enough to be used in any kind of application. 'prependWidget' is an example of server-side control within the widget, to add dynamic behaviour. There is a 'ajaxSend' primitive that permits to send/receive many ajax messages during the same ajax procedure. It is intended that with this kind of server-side control, a single page with widgets coordinated in this way can present complex behaviours to the user.

The result is that the programmer can define general widgets at a level equal or higher than  ASP.NET server side controls or the JavaServer Faces with type safety. The price of the deep-first approach for accumulating levels of programming as fast as possible is the lack of stability (in fact the code above has changed) but the benefit is that the design updates will not be based on artificial tests, as a result of some assumptions, but in the use of  real application with real users.
In the next post I will show some usage examples of this widget 
This example uses the last version of MFlow at https://github.com/agocorona/MFlow.
It uses the last version of  Workflow  https://github.com/agocorona/Workflow

Post a Comment