Showing posts with label Monads. Show all posts
Showing posts with label Monads. Show all posts

Thursday, June 20, 2013

The promising land of monadic formlets. Or: "Look ma! no JavaScript!"



Formlets are about applicative instances, but what about monadic instances? What a Monad instance of formlets means? I recently experimented with this and the results are very interesting -and powerful-. It mixes the best of web forms, with the flexibility of console applications.

???!!!!!!

What I mean with that? There are two fundamental questions for usability and high level programming of user interfaces.  Here are with my responses:

  1. What is the best interface? A dynamic, window-oriented or document oriented interface for some applications  and optional console-style presentation and interaction for others
  2. What is the most intuitive way of programming interfaces? The sequential style of console applications.

What if I say that monadic formlets have the potential to realize the two (or three) whishes with no compromises?
 
Let´s look at an example. There is a video of the execution below. Although this example is for the formlets of the MFlow framework , it can be ported to other formlet implementations. The MFLow formlets includes operators for web formatting that is not supported in other formlets implementations. Static HTML templating don´t work well with monadic formlets, so it is important to include the formatting as a part of the computation:

formWidget= wform $ do
      (n,s) <- (,) <$> p << "Who are you?"
                   ++> getString Nothing  <! hint "name"     <++ br
                   <*> getString Nothing  <! hint "surname"  <++ br
                   <** submitButton "ok" <++ br
                   
      flag <- b << "Do you " ++> getRadio[radiob "work?",radiob "study?"] <++ br
      
      r<-case flag of
         "work?" -> pageFlow "l"
                     $ Left  <$> b << "do you enjoy your work? "
                             ++> getBool True "yes" "no" 
                             <** submitButton "ok"  <++ br
                             
         "study?"-> pageFlow "r"
                     $ Right <$> b << "do you study in "
                             ++> getRadio[radiob "University"
                                         ,radiob "High School"]
      u <-  getCurrentUser                                     
      p << ("You are "++n++" "++s) ++>
       p << ("And your user is: "++ u) ++>
       case r of
         Left fl ->   p << ("You work and it is " ++ show fl ++ " that you enjoy your work")
                        ++> noWidget

         Right stu -> p << ("You study at the " ++ stu)
                        ++> noWidget


hint s= [("placeholder",s)]
onClickSubmit= [("onclick","this.form.submit()")]
radiob s n= text s ++&gt setRadio s n <! onClickSubmit


 
 Here wform, getBool, getString , getRadio etc are formlet elements


The first sentence is an applicative composition that generate a 2 tuple, to show that applicative and monadic can be mixed.  The operations ++> add html to the formlet. The operatior <! add attributes to the formlet element.. noWidget is a dumb formlet that does not validate.

The second monadic statement is an election between two options. The beauty of the monadic instance is that the rest of the form can vary depending on the previous answers. Since the formlets validate the input, unless the election is made, the radio will not validate, so the monadic execution will be aborted beyond any unanswered question, so nothing will appear after the question. The rest of the form will appear when the user choose one of the two options. once one or the other option is chosen, then another binary question is presented. (either he likes his work or where he study). When the questions are finised, the results are presented. This kind of presentation is similar to what we would see in a console application.

I hope that you get the idea. The benefit is not only the familiar coding and presentation of a sequential console application: Since the form encloses all the fields, At any time the user can change previous inputs and the form will reflect these changes. For example if the user change from work to study (second statements) the "where do you study will appear and the work related questions and answers will disappear. That is wonderfully useful for heavily interactive applications.

There is a problem however and it is the issue of the formlet identifiers. Unlike in an applicative presentation, now the number and type of the formlets will vary, so the response to a previous form create a new kind of form with different fields. And, because the form identifiers, assigned sequentially, vary, the post response can be misinterpreted. To avoid that , the pageFlow call creates fixed sequences of identifiers for each branch of execution.

I will release a version of MFlow that support this kind of monadic composition of fomlets, but In essence it is nothing but a Monad instance for formlets. A single server procedure, that executes the formlet code can support all the interaction so any framework can do it. The usability of that is huge: It is possible to interact in a web page in a console style with questions and answers with the versatitly of a dynamic foms: Any modification in the form change the subsequent flow of interaction. Another application of this monadic style is to ease multistep processes such are registration, check-out and payment ad so on. Even a entire interactive dynamic application can be coded in a single page.

And no javascript is needed!.

This page flow is simple, but imagine a flow where the first line includes formlets for tabs or menus An entire application can be controlled in this way.


To run this formlet in MFlow:

main=do

  addMessageFlows
       [(""    , transient $ runFlow  $ ask dynamicForm )]

  wait $ run port waiMessageFlow

 
This video show how the presentation of this example vary with the user input:



 This other video has a better resolution:
 


I hope that you find the idea interesting. If you want to experiment with this in MFlow, I have to say that the implementation of this feature is in an early stage. The code is in the head branch
 

 
The code of the example, that was executed in the video, is part of a demo:
https://github.com/agocorona/MFlow/blob/head/Demos/demos.blaze.hs

The next step: Selective refreshing.

Dinamic formlets are composable in MFlow . The video shows a counter, a login widget and this formlet working in the same page (see the code of these widgets in previous posts) But the entire page must be refreshed to achieve dynamic behavior. The next step is to try to avoid the need to refreshing the entire page by using ajax to refresh just the widget that has changed. This is halfway done now, since this functionality is available for some particular widgets. it is a matter of generalizing the mechanism.

Sunday, July 01, 2012

From Monads to Monoids in a small category


(Added 06/09/12: clarification about the nature of ' m a' from the point of view of C.Theory)

Let's start with the definition of a small category with a morphism defined by 'm' between set of objects 'a' and 'm b'. The simplest category definition would be:

{-# LANGUAGE FlexibleInstances #-}
import Data.Monoid
import Control.Monad

class Category  m a b where
 morph:: a ->  m b


Here 'm b'  is the codomain of the morphism which depends on the category instance. More on this at the end of the post. .

The other two requisites for a category according with this are an identity operation and associativity of the morphism. The first is guaranteed in Haskell by the polymorphic function 'id'.


id :: a -> a
id x = x

Lett´s modify the signature of 'id' slightly to match the Category definition as such:

return :: a -> m a

The second condition, associativity, is guaranteed by the nature of the forward chaining of operations in any programming language. (if not where that way, it would be impossible the denotational semantics of imperative languages in terms of monads, I guess)



The definition of functor According to this :



Let C and D be categories. A functor F from C to D is a mapping that:

  • associates to each object  an object ,
  • associates to each morphism  a morphism


So the morphism (a -> m b) may meet the first condition. This morphism  maps elements from the set ‘a’ to elements in the set ‘m b’. The second condition is the one defined in the Haskell instance of functor:


instance Functor a where

 fmap :: (a -> b) -> (m a ->  m b)


When a and b are the same, then we have a functor.which maps elements in ‘a’ to elements in ‘m a’  (a -> m a). But are 'a' and 'm a' the same? It seems that it is not the case, but I will talk about it later.

The functor category has functors as elements and natural transformations as morphisms, Additionally, the functors have a double nature as maps between points in ‘a’ (a -> ma) and as maps between morphisms (fmap) .  But a monoid is defined over elements of the set, and a monad works with morphisms (a -> m b), so we are interested on the set of elements with signature  (a -> m a) , to describe the Monoid instance for these elements.


If we try to construct the Monoid instance for any morphisms (a -> m a) . This instance demands that 'm' is a monad, that is, that the morphisms of m must  compose according with the monad laws:


instance Monad m => Monoid  (a -> m a) where
   mappend f g= \x -> do
                          y <-  f x
                          g y
   mempty=   \x -> return x

The definition of mappend is equivalent to the Kleisli operator in a Monad

(>=>) :: (a -> m b) -> (b -> m c) -> (a -> m c)
f >=> g = \x -> f x >>= g


In this case, a b and c are the same sets:

The Monoid instance e of morphisms that meet the Monad laws can be written as:

instance Monad m => Monoid  (a -> m a) where
   mappend = (>=>)
   mempty  = return

This typechecks in Haskell. But does this set (a -> m a) of maps between elements from the endofunctors have the necessary  reflexive and associative properties for being a Monoid? If you compare (1) the asociativite and identity laws of a Monoid, when applied to the morphisms between (a -> m a)  objects,  with (2) the monad laws you will conclude that (1) and (2)  are identical, and thus only the monadic morohisms form a Monoid,

The Monoid is defined within the subset of morphisms (a -> m a) which are part of the functor category, not with the set 'a' as such.  The return operation in a Monad correspond with the identity morphism in the set of such  morphisms. 



While in the IO monad, the morphisms (a -> IO a) refer to the same set ‘a’ in the domain and the codomain, (a -> Maybe a)  has one additional element (Nothing) which is terminal. 

In the case of List monad, the morphisms of the monad (a -> [a]) 'm a' is [a]. It sems quite different at first sight, But a list, as seen from the point of view of the list category, is a list of alternative arrows in the set 'a' (see the previous post).

for example the expression

f :: String -> [String]
f x= take 5 $ repeat x

can be considered as a endomorphism in the set of Strings that has five arrows. Then 'f' can be considered as a morphisn from 'a' to the set 'a' plus the empty list element (which is terminal)

'What is 'm a’ ?. Seen from the point of view of category theory, it is 'a' plus some terminal element(s).

What about the generalization for the morphisms of any ( a -> m b) being a and b of any kind? The Monoid instance can be extended to a larger class of morphisms between any Typeabe objects by assuming a of type Dynamic. So implicitly, to any a -> m b as long as a and b are Typeable:


instance Monad m => Monoid  (Dynamic -> m Dynamic) where
   mappend = (>=>)
   mempty  = return
If any 'a' with morphisms (a -> m a) is a Category, Then, I guess, the set of Typeable objects with the morphisms (Dynamic -> m Dynamic) is a Category

Even it can be extended to the endofunctors of the set of elements of any type:

data AllTypes = forall a . AllTypes a


instance Monad m => Monoid  (AllTypes-> m AllTypes) where
   mappend = (>=>)
   mempty  = return


Which implicity is a Monoid instance for any (a -> m b)

Am I wrong?. Did I miss something?