980 likes | 1.42k Views
i-Tasks - i nteractive workflow Tasks for the WWWEB ___________ Rinus Plasmeijer University of Nijmegen www.cs.ru.nl/~clean Clean Introduction Defining Interactive Multi-user Workflow Systems for the web Defining a simple task: an editor for a web form Combinators for constructing tasks
E N D
i-Tasks-interactive workflow Tasksfor the WWWEB___________ Rinus Plasmeijer University of Nijmegen www.cs.ru.nl/~clean
Clean • Introduction • Defining Interactive Multi-user Workflow Systems for the web • Defining a simple task: an editor for a web form • Combinators for constructing tasks • Assigning tasks to users • Logging in to a Multi-user Workflow System • Implementation • Basic idea of generic programming • Generic functions for handling web forms: i-Data • Implementation of i-Tasks • Conclusion & Future Research
Clean • Introduction • Defining Interactive Multi-user Workflow Systems for the web • Defining a simple task: an editor for a web form • Combinators for constructing tasks • Assigning tasks to users • Logging in to a Multi-user Workflow System • Implementation • Basic idea of generic programming • Generic functions for handling web forms: i-Data • Implementation of i-Tasks • Conclusion & Future Research
Clean • State-Of-The-Art Pure Functional Programming Language • Lazy, pure, higher order functions and types, lots of features • Clean is an extended subset of Haskell – de-facto standard (GHC) • Haskell is an extended subset of Clean • Extra facilities in Clean: • I/O: Uniqueness Typing <-> Monads • Re-usage: Generic programming included <-> Generic Haskell preprocessor • Hybrid typing: Static as well as Dynamic typing <-> poor man's Dynamics • Type safe plug-inns: run-time storing and loading of functions • Sparkle: Cleans dedicated theorem prover • Gast: Cleans test system • Clean Compiler is fast (4th place language shootout, after 3 C compilers !) • 2 third party Haskell -> Clean compilers • Haskell / Clean code can be combined in next Clean release
Why workflows? • Dutch STW grant “Demand Driven Workflows” • New approach on specifying workflows making use of "lazy evaluation" • University: TU Eindhoven (Van der Aalst) • RadBoud University Nijmegen (Plasmeijer) • Industry: Palas Athena (Eindhoven): producer of commercial workflow system • ABZ (Utrecht): uses workflow system for developing applications • AIA (Nijmegen): produces content management systems • i -Tasks • First, "simple" approach to make a quick start • Already offers more functionality than found in commercial systems • “i –Tasks: Executable Specifications of Interactive Workflow Systems for the Web”,R.Plasmeijer, P.Achten, P.Koopman, ICFP 2007. • see: http://www.cs.ru.nl/~rinus/iTaskIntro.html
i -Tasks Approach I • Study “Workflow Patterns” (Van der Aalst, ter Hofstede, Kiepuszewski, Barros) • > 30 products: Staffware, Cosa, InConcert, Eastman Software, FLOWer, Domino Workflow, Meteor, Mobile, MQSeries, Forte Conductor,Verve, Visual WorkFlo, Changengine, I-Flow, SAP R/3 Workflow • patterns: sequence, recursion, exclusive choice, multiple choice, split/merge (parallel or, parallel and, discriminator), ... • All Workflow Patterns can "straightforwardly" be implemented in Clean • Using i-Data: Clean library for handling interactive web forms • Using generic functions: highly reusable functions, given a type they • generate an html form • deal with any change made by a user in a form • enable separation between model (value returned) and view (the looks) • automatically store and retrieve info in a file or database
i -TasksApproach II • Disadvantages i –Tasks over Commercial Systems • No nice graphical interface for defining workflows: just Clean code • A first prototype, limited interfaces to real world, lots of additional wishes • Advantages i –Tasks over Commercial Systems • Declarative, executable specification • Workflows are statically typed, input type checked as well • Highly reusable code: polymorphic, overloaded, generic • Workflows are dynamically constructed • Flow can depend on the actual contents • Fully compositional • Higher order tasks: shift work flows to someone else • It generates a multi-user web enabled (!) workflow system • < 1000 lines of code based on Clean’s i-Data library for the web
i -Tasks Approach III • Web applications are not easy to write • Interactive applications for the Web are hard to programme • No direct connection between User on Client <--> Application on Server • Web has no notion of state: application has to store information • Multi-user applications even harder • Offer a layer which hides as many annoying details as possible. • i –Tasks - Embedded Domain Specific Language: • Workflow Specification Language which generates a multi-user system for the web. • i –Tasks – Workflow Combinator Library for Clean
Clean • Introduction • Defining Interactive Multi-user Workflow Systems for the web • Defining a simple task: an editor for a web form • Combinators for constructing tasks • Assigning tasks to users • Logging in to a Multi-user Workflow System • Implementation • Basic idea of generic programming • Generic functions for handling web forms: i-Data • Implementation of i-Tasks • Conclusion & Future Research
A very small *complete* example I • module example • import StdEnv, StdHtml • Start world = doHtmlServer (singleUserTask 0 True simple) world • simple:: (Task Int) • simple = editTask "Done" createDefault
Testing an i –Tasks application Browser Http 1.0 Web Server i –Tasks Application Changes in Forms Htmlcode Clean Application
Final setting of an i –Tasks application Browser Http 1.1 Web Server i –Tasks Application 2 i –Tasks Application n i –Tasks Application 1 Changes in Forms Htmlcode
A very small *complete* example II • module example • import StdEnv, StdHtml • Start world = doHtmlServer (singleUserTask 0 True simple) world • simple:: (Task (Int, Real)) • simple = editTask "Done" createDefault
A very small *complete* example III • simple:: (Task [Int]) • simple = editTask "Done" createDefault
A very small *complete* example IV • :: Person = { name :: String • , street :: String • , number :: Int • , zipCode :: String • , town :: String • , born :: HtmlDate • } • simple:: (Task Person) • simple = editTask "Done" createDefault • derive gForm Person • derive gUpd Person • derive gParse Person • derive gPrint Person • derive gerda Person
editTask • editTask :: String a (Task a) | iData a// an editor for values of type "a" • :: Task a // an interactive task • A task consist of an amount of work to be performed by the user involving ≥ 0 interactions • It is either not active, active, or finished.
editTask uses generic functions • class iData a | gForm {|*|} , iCreateAndPrint, iParse, iSpecialStorea • class iCreateAndPrint a | iCreate, iPrinta • class iCreate a| gUpd {|*|} a • class iPrint a| gPrint {|*|} a • class iParse a| gParse {|*|} a • class iSpecialStorea| gerda {|*|}, TCa • It requires the instantiation of several generic functions for type "a" e.g. • gForm html form creation, • gUpd form handling, • gParse parsing, • gPrint printing, • gerda data storage I a relational database, • TC Conversion to and from Dynamics, option used to store functions • which can all, on request, automatically be derived by the compiler !
Options • A task or combination of tasks, can have several options: • class (<<@) infixl 3 b :: (Task a) b Task a • instance<<@Lifespan // default: Session • , StorageFormat // default: PlainString • , Mode // default: Edit • , GarbageCollect // deafult: Collect • :: Lifespan =Database | TxtFile | Session | Page | Temp • :: StorageFormat =StaticDynamic | PlainString • :: Mode =Edit | Submit | Display | NoForm • :: GarbageCollect = Collect|NoCollect
A very small *complete* example IV • simple:: (Task Person) • simple = editTask "Done" createDefault
A very small *complete* example IV Submit • simple:: (Task Person) • simple = editTask "Done" createDefault <<@ Submit
A very small *complete* example IV, Submit, Database • simple:: (Task Person) • simple = editTask "Done" createDefault <<@ Submit <<@ Database
A very small *complete* example IV, Submit, TxtFile • simple:: (Task Person) • simple = editTask "Done" createDefault <<@ Submit <<@ TxtFile
Clean • Introduction • Defining Interactive Multi-user Workflow Systems for the web • Defining a simple task: an editor for a web form • Combinators for constructing tasks • Assigning tasks to users • Logging in to a Multi-user Workflow System • Implementation • Basic idea of generic programming • Generic functions for handling web forms: i-Data • Implementation of i-Tasks • Conclusion & Future Research
Sequencing of tasks • Sequencing / composition of tasks (monadic style): • (=>>) infix 1 :: (Task a) (a Task b) Task b • (#>>) infixl 1 :: (Task a) (Task b) Task b • Returning plain values as a Task value • return_V :: a Task a | iData a
Prompting • Returning plain values as a Task value (showing the returned value): • return_D :: a Task a | iData a • Prompting as long as / as soon as a task is activated: • (?>>) infix 5 :: [BodyTag] (Task a) Task a | iData a • (!>>) infix 5 :: [BodyTag] (Task a) Task a | iData a
Html code • A Clean Algebraic Data Type (ADT) is defined isomorphic with Html Code • Provides a grammar only allowing syntactic "correct" html code • Type error otherwise • A generic function is used to generate Html code out of it • :: BodyTag • =A [A_Attr] [BodyTag] // link ancor <a></a> • | Abbr [Std_Attr] String // abbreviation <abbr></abbr> • | Acronym [Std_Attr] String // acronym <acronym></acronym> • | Address [Std_Attr] String // address <address></address> • | Applet [Applet_Attr] String // applet <applet></applet> • | Area [Area_Attr] // link area in an image <area> • | B [Std_Attr] String // bold <b></b> • … • | Txt String // plain text • | U [Std_Attr] String // underlined text <u></u> • | Ul [Ul_Attr] [BodyTag] // unordered list <ul></ul> • | Var [Std_Attr] String // variable text <var></var>
Sequence of iTasks • sumInt:: (Task Int) • sumInt • = editTask "Done" createDefault • =>> \v1editTask "Done" createDefault • =>> \v2 [Txt "+", Hr [] ] • !>>return_D (v1 + v2)
Simple Coffeemachine (2/3) • simpleCoffee= foreverTask SimlpeCoffeeMachine • SimlpeCoffeeMachine:: (Task String) • SimlpeCoffeeMachine • = [Txt "Choose product:",Br,Br] • ?>>chooseTask • [ ("Coffee", return_V ("Coffee")) • , ("Tea", return_V ("Tea")) • ] • =>> \product [Txt ("Enjoy your " <+++ product)] • ?>>chooseTask • [ "OK“, return_Vproduct]
Simple Coffeemachine (3/3) • simpleCoffee= foreverTask SimlpeCoffeeMachine • SimlpeCoffeeMachine:: (Task String) • SimlpeCoffeeMachine • = [Txt "Choose product:",Br,Br] • ?>>chooseTask • [ ("Coffee", return_V ("Coffee")) • , ("Tea", return_V ("Tea")) • ] • =>> \product [Txt ("Enjoy your " <+++ product)] • ?>>buttonTask "OK" (return_Vproduct)
All kinds of task combinators • Loop: • foreverTask:: (Task a) (Task a) | iData a • Choose 1 out of n: • chooseTask:: [(String,Task a)] (Task a) | iData a • Choose m out of n: • mchoiceTasks :: [(String,Task a)] (Task [a]) | iData a • Or task, do all in any order, finish as soon as one completes • (-||-) infixr 3:: (Task a) (Task a) (Task a) | iData a • orTasks:: [(String,Task a)] (Task a) | iData a • And task, do all in any order, and finish when all completed • (-&&-) infixr 4:: (Task a) (Task b) (Task (a,b)) | iData a & iData b • andTasks:: [(String,Task a)] (Task [a]) | iData a • Treat user defined function as a new task: enables recursion • newTask:: String (Task a) (Task a) | iData a
Coffeemachine (2/3) • infCoffee= foreverTask CoffeeMachine • CoffeeMachine:: Task (String, Int) • CoffeeMachine • = [Txt "Choose product:", Br, Br] • ?>>chooseTask • [ ("Coffee: 100", return_V (100,"Coffee")) • , ("Cappucino: 150", return_V (150,"Cappucino")) • , ("Tee: 50", return_V (50, "Tee")) • , ("Choclate: 100", return_V (100,"Choclate")) • ] • =>> \(toPay, product) [Txt ("Chosen product: " <+++ product), Br, Br] • ?>>getCoins (toPay, 0) • =>> \(cancel, returnMoney) • let nproduct =if cancel "Cancelled" product in • [Txt ("product = " <+++ nproduct <+++ ", • returned money = " <+++ returnMoney), Br, Br] • ?>>buttonTask"Thanks" (return_V (nproduct, returnMoney))
Coffeemachine (3/3) • getCoins:: (Int, Int) -> Task (Bool, Int) • getCoins (toPay, paid) = newTask "getCoins" getCoins` • where • getCoins` • = [Txt ("To pay: " <+++ toPay), Br, Br] • ?>>chooseTask [ (c +++> " cts", return_V (False, c)) \\ c coins ] • -||- • buttonTask"Cancel" (return_V (True, 0)) • =>> handleMoney • handleMoney (cancel, coin) • | cancel = return_V (True, paid) • | toPay - coin > 0 = getCoins (toPay - coin, paid + coin) • | otherwise = return_V (False, coin - toPay) • coins = [ 5, 10, 20, 50, 100, 200 ]
Clean • Introduction • Defining Interactive Multi-user Workflow Systems for the web • Defining a simple task: an editor for a web form • Combinators for constructing tasks • Assigning tasks to users • Logging in to a Multi-user Workflow System • Implementation • Basic idea of generic programming • Generic functions for handling web forms: i-Data • Implementation of i-Tasks • Conclusion & Future Research
Multi-user combinators • Multi-user : • (@:) infix 3 :: (String, Int) (Task a) (Task a)| iData a • (@::) infix 3 :: Int (Task a) (Task a)| iData a
Review Task (1/3) • :: Review =Approved|Cancelled|NeedsRework TextArea |Draft • reviewTask:: a (Task Review) | iData a • reviewTask v • = [toHtml v, Br, Br] • ?>>chooseTask • [ ("Rework", editTask "Done" (NeedsRework createDefault) <<@ Submit) • , ("Approved", return_VApproved) • , ("Reject", return_VRejected) • ]
Review Task (2/3) • taskToReview:: Int (a, a Task a) Task (a, Review) | iData a • taskToReviewreviewer (val, task) • = newTask "taskToReview" taskToReview` • where • taskToReview` • = task val • =>> \newvalreviewer@::reviewTask newval • =>> \review [Txt ("Reviewer " <+++ reviewer <+++ " says ") , toHtml review, Br] • ?>>editTask "OK" Void • #>>casereviewof • (NeedsRework _) taskToReviewreviewer (newval, task) • else return_V (newval, review)
Review Task (3/3) • :: QForm = { toComp :: String • , startDate :: HtmlDate • , endDate :: HtmlDate • , estimatedHours :: Int • , description :: TextArea • , price :: Real • } • startTask:: Task (QForm, Review) • startTask = taskToReview1 (createDefault, mytask) • mytask:: a (Task a) | iData a • mytask v = [Txt "Fill in Form:", Br, Br] • ?>>editTask "TaskDone" v <<@ Submit
Higher-Order Tasks • Tasks not only deliver values, they may deliver a task under development ! • :: TClosure a =TClosure (Task a) • :: Maybe a =Just a | Nothing • orTask variant: a task is either finished, or interrupted if the stop task is finished sooner • (-!>) infix 4 :: (Task stop) (Task a) (Task (Maybe stop, TClosure a)) | iData stop & iData a
Delegate a Task • delegateToSomeone:: Int (Task a) [Int] (Task a)| iData a • delegateToSomeoneme task set = newTask "delegateToSomeone" doDelegate • where • doDelegate • = orTasks [ ("Waiting for " <+++ who • , who@::chooseTask [("I Will Do It“, return_V who)] • ) \\ who set • ] • =>> \volunteervolunteer@::stopIt-!> task • =>> \(stopped, TClosure task) • if (isJust stopped) (delegateToSomeonemetask set) task • stopIt= stop-||- (me@::stop) • stop= chooseTask [("Stop“, return_V True)]
Clean • Introduction • Defining Interactive Multi-user Workflow Systems for the web • Defining a simple task: an editor for a web form • Combinators for constructing tasks • Assigning tasks to users • Logging in to a Multi-user Workflow System • Implementation • Basic idea of generic programming • Generic functions for handling web forms: i-Data • Implementation of i-Tasks • Conclusion & Future Research
Login handling types and functions … • :: Accounts s :== [Account s] • :: Account s = { login :: Login // login info • , uniqueId :: Int // unique identifier • , state :: s // state • } • :: Login = { loginName :: String // Should be unique • , password :: PasswordBox // Should remain secret • } • :: Maybe a = Just a | Nothing • addAccount :: (Account s) (Accounts s) (Accounts s) • removeAccount :: (Account s) (Accounts s) (Accounts s) • changeAccount :: (Account s) (Accounts s) (Accounts s) • hasAccount :: Login (Accounts s) (Maybe (Account s)) • invariantLogins :: String [Login] Maybe (String,String)
iTasks can be used for persistent storage of information • definition module iTaskDB • import iTasks • :: DBid a • mkDBid:: String Lifespan (DBid a) • readDB:: (DBid a) Task a | iData a • writeDB:: (DBid a) a Task a | iData a
Creating a database for a login accounts… • accountId :: DBid (Accounts a) • accountId= mkDBid "loginAccount" TxtFile • readAccountsDB :: (Task (Accounts a)) | iData a • readAccountsDB= readDB accountId • addAccountsDB :: (Account a) (Accounts a) (Task (Accounts a)) | iData a • addAccountsDBacc accs = writeDB accountId (addAccount acc accs)
Creating a database for a login accounts… • accountId :: DBid (Accounts a) • accountId= mkDBid "loginAccount" Database • readAccountsDB :: (Task (Accounts a)) | iData a • readAccountsDB= readDB accountId • addAccountsDB :: (Account a) (Accounts a) (Task (Accounts a)) | iData a • addAccountsDBacc accs = writeDB accountId (addAccount acc accs)
Creating a database for a login accounts… • :: Void =Void • accountId :: DBid (Accounts Void) • accountId= mkDBid "loginAccount" TxtFile • readAccountsDB :: (Task (Accounts Void)) • readAccountsDB= readDB accountId • addAccountsDB :: (Account Void) (Accounts Void) (Task (Accounts Void)) • addAccountsDBacc accs = writeDB accountId (addAccount acc accs)