Tuesday, September 18, 2012

ANNOUNCE MFlow 0.1.5 Web app server for stateful processes with safe, composable user interfaces.


MFlow is a is a Web framework with some unique, and I mean unique,characteristics that I find exciting:

- It is a Web application server that start and restart on-demand stateful web server processes (not request.-response). This means that all the page navigation can be coded in a single procedure. This increases readability of the programmer code. I woul call it a anti-node.js.  Buit usual request-response (stateless) server processes are also allowed

- When the process is invoqued as result of an URL request, the Web app server not only restart the process but also recover its execution state. The enclosing Workflow monad provides the thread state persistence. There are state timeouts and process timeouts defined by the programmer. Processes with no persistent state (transient) are possible.

The user interface is made of widgets. They are  formlets with added formatting,   attributes, validations, modifiers and callbacks, that are composable, so the pieces are reusable and return type safe responses to the calling process. Even the links are part of widgets and return back type safe inputs at compile time to the calling server process. Tho glue these components, ordinary applicative combinators and other extra combinators are used.

- The widgets and the communication don´t make assumptions about the architecture, so it can be adapted to non-web environments. This versions has interface for WAI-warp, Hack, Text.XHtml (xhtml) , and Haskell Server Pages.

- The widget rendering can be converted to ByteStrings automatically with special combinators. A mix of widgets with different formats can be combined in the same source file. For example Text.Html and HSP (Haskell server pages)

-These widgets can be cached, to avoid widget rendering on every interaction.

-To handle the back button in web browsers, and because the processes are stateful, they can run backwards until the response match. This is transparent for the programmer, thanks to the embedded FlowM monad.

-All the programmer coding in pure Haskell. No deployment, special scripts, formats etc are necessary.

-Besides automatic state persistence, TCache provides transactions and user data persistence, that can be configured for SQL databases. Default persistence in files permit very rapid prototyping. Just code and run it with runghc.

-Has AJAX support

All of this sounds very complicated, but really it is simple!. Most of these things are transparent. The resulting code is quite readable and has very little plumbing!

There is a non trivial example that some of these functionalities embedded here that you can run:


Take a look and tell me your opinion.  I hope that you find it as exciting as me.

Although still it is experimental, it is being used in at least on future commercial project. So I have te commitment to continue its development. There are many examples in the documentation and in the package.


 I´m looking for people  to collaborate in the development of MFlow. You are welcome!.



This is the example:


{-# LANGUAGE ScopedTypeVariables, DeriveDataTypeable #-} 
module Main where
 import MFlow.Wai.XHtml.All
 import Data.TCache
 import Control.Monad.Trans
 import Data.Typeable
 import Control.Concurrent
 import Control.Exception as E
 import qualified Data.ByteString.Char8 as SB
 import qualified Data.Vector as V
 import Data.Maybe

 data Ops= Ints | Strings | Actions | Ajax | Opt deriving(Typeable,Read, Show)
 main= do
    setFilesPath ""
    addFileServerWF
    addMessageFlows [(""  ,transient $ runFlow mainf)
                    ,("shop"    ,runFlow shopCart)]
    forkIO $ run 80 waiMessageFlow
    adminLoop

 stdheader c= p << "you can press the back button to go to the menu"+++ c

 mainf=   do
        setHeader stdheader
        r <- ask $   wlink Ints (bold << "increase an Int")
                <|>  br ++> wlink Strings (bold << "increase a String")
                <|>  br ++> wlink Actions (bold << "Example of a string widget with an action")
                <|>  br ++> wlink Ajax (bold << "Simple AJAX example")
                <|>  br ++> wlink Opt (bold << "select options")
                <++ (br +++ linkShop) -- this is an ordinary XHtml link

        case r of
          Ints    ->  clickn 0
          Strings ->  clicks "1"
          Actions ->  actions 1
          Ajax    ->  ajaxsample
          Opt     ->  options
        mainf
     where
     linkShop= toHtml $ hotlink  "shop" << "shopping"

 options= do
    r <- ask $ getSelect (setOption "blue" (bold << "blue")   <|>
                          setSelectedOption "Red"  (bold << "red")  ) <! dosummit
    ask $ p << (r ++ " selected") ++> wlink () (p<< " menu")
    breturn()
    where
    dosummit= [("onchange","this.form.submit()")]

 clickn (n :: Int)= do
    setHeader stdheader
    r <- ask $  wlink "menu" (p << "menu")
            |+| getInt (Just n) <* submitButton "submit"
    case r of
     (Just _,_) -> breturn ()
     (_, Just n') -> clickn $ n'+1


 clicks s= do
    setHeader stdheader
    s' <- ask $ (getString (Just s)
              <* submitButton "submit")
              `validate` (\s -> return $ if length s   > 5 then Just "length must be < 5" else Nothing )
    clicks $ s'++ "1"


 ajaxheader html= thehtml << ajaxHead << p << "click the box" +++ html

 ajaxsample= do
    setHeader ajaxheader
    ajaxc <- ajaxCommand "document.getElementById('text1').value"
                         (\n ->  return $ "document.getElementById('text1').value='"++show(read  n +1)++"'")
    ask $ (getInt (Just 0) <! [("id","text1"),("onclick", ajaxc)])
    breturn()

 actions n=do
   ask $ wlink () (p << "exit from action")
      <**((getInt (Just (n+1)) <** submitButton "submit" ) `waction` actions )
   breturn ()

 -- A persistent flow  (uses step). The process is killed after 10 seconds of inactivity
 -- but it is restarted automatically. if you restart the program, it remember the shopping cart
 -- defines a table with links enclosed that return ints and a link to the menu, that abandon this flow.
 shopCart  = do
    setTimeouts 10 0
    shopCart1 (V.fromList [0,0,0:: Int])
    where
    shopCart1 cart=  do
      i <- step . ask $
              table ! [border 1,thestyle "width:20%;margin-left:auto;margin-right:auto"]
              <<< caption << "choose an item"
              ++> thead << tr << concatHtml[ th << bold << "item", th << bold << "times chosen"]
              ++> (tbody
                   <<<  tr ! [rowspan 2] << td << linkHome
                   ++> (tr <<< td <<< wlink  0 (bold <<"iphone") <++  td << ( bold << show ( cart V.! 0))
                   <|>  tr <<< td <<< wlink  1 (bold <<"ipad")   <++  td << ( bold << show ( cart V.! 1))
                   <|>  tr <<< td <<< wlink  2 (bold <<"ipod")   <++  td << ( bold << show ( cart V.! 2)))
                   <++  tr << td << linkHome
                   )

      let newCart= cart V.// [(i, cart V.! i + 1 )]
      shopCart1 newCart
     where
     linkHome= (toHtml $ hotlink  noScript << bold << "home")
Post a Comment