Tuesday, April 30, 2013

Type safety is not enough for safe Web development

 
A response in Stack Overflow about Looking for a type-safe web development platform
 
Type safeness does not solve all the problems and errors that an application may have. The main problems of web development is the stateless nature of HTTP, that forces an event handling programing model, full of unsafe identifiers of variables and event handlers refered here and there. The state most of the time is in the form of hash-tables of dynamically typed data, since the event handlers do not share variable scopes. Therefore state management is a nightmare, and code readability is not good since the event handling model is a design level goto.
 
Here, strong types do not help, except in the case of continuation based frameworks, like ocsigen (ocaml) and seaside (smalltalk). They handle nicely the back button, they keep state in normal variables and the navigation can be understood by reading the code. And they are mostly RESTFul to a certain level. But these frameworks are not scalable and have persistence problems by the inherent problems of continuations.
 
The other problem of web applications is the typeless nature of HTML, which can produce mismatches and runtime errors. But in this case all the Haskell web frameworks and many others do a fair job.
 
In MFlow not only each page, but the entire navigation is safe at compile time and it does not share the above problems. It has the nice properties of continuation based frameworks, but it is scalable, since it uses logging and backtracking instead of continuations. It uses standard Haskell web libraries: WAI, formlets, stm, blaze-html. It has a system of pluggable self-contained components.
 
This is a complete application with three pages. In a loop, it ask for two numbers and show the sum. you can press the back button as you please. There is no magic identifiers that you have to put here an there, in configuration files, pages and source code:
 
module Main where
import MFlow.Wai.Blaze.Html.All

main= do
  addMessageFlows  [("sum", transient . runFlow $ sumIt )]
  wait $ run 8081 waiMessageFlow

sumIt= do
  setHeader $ html . body
  n1 <- ask $  p << "give me the first number" 
               ++> getInt Nothing 
               <** submitButton "send"

  n2 <- ask $  p << "give me the second number" 
               ++> getInt Nothing 
               <** submitButton "send"

  ask $ p << ("the result is " ++ show (n1 + n2)) ++> wlink () << p << "click here"
  
 
The state can be made persistent with little modifications (by adding "step . ask", instead of ask,  and deleting "transient ." )

No comments: