i-Tasks - interactive workflow tasks for the WEB
Download
Report
Transcript i-Tasks - interactive workflow tasks for the WEB
i-Tasks
interactive 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
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
2
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
3
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
Haskell is an extended subset of Clean
– de-facto standard (GHC)
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
4
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
5
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
6
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
7
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
8
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
9
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
10
Testing an i –Tasks application
Browser
Changes in Forms
Htmlcode
Http 1.0 Web Server
Clean Application
i –Tasks Application
11
Final setting of an i –Tasks application
Browser
Htmlcode
Changes in Forms
Http 1.1 Web Server
i –Tasks Application 1
i –Tasks Application 2
i –Tasks Application n
12
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
13
A very small *complete* example III
simple :: (Task [Int])
simple = editTask "Done" createDefault
14
A very small *complete* example IV
:: Person = { name
, street
, number
, zipCode
, town
, born
}
:: String
:: String
:: Int
:: String
:: String
:: HtmlDate
simple :: (Task Person)
simple = editTask "Done" createDefault
derive
derive
derive
derive
derive
gForm Person
gUpd Person
gParse Person
gPrint Person
gerda Person
15
editTask
editTask :: String a (Task a)
:: Task a
| iData a
// an editor for values of type "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.
16
editTask uses generic functions
class
class
class
class
class
class
iData a
iCreateAndPrint a
iCreate a
iPrint a
iParse a
iSpecialStore a
|
|
|
|
|
|
gForm {|*|} , iCreateAndPrint, iParse, iSpecialStore a
iCreate, iPrint a
gUpd {|*|} a
gPrint {|*|} a
gParse {|*|} a
gerda {|*|}, TC a
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 !
17
Options
A task or combination of tasks, can have several options:
class (<<@) infixl 3 b :: (Task a) b Task a
instance <<@
,
,
,
:: Lifespan
:: StorageFormat
:: Mode
:: GarbageCollect
Lifespan
StorageFormat
Mode
GarbageCollect
//
//
//
//
default:
default:
default:
deafult:
Session
PlainString
Edit
Collect
= Database | TxtFile | Session | Page | Temp
= StaticDynamic | PlainString
= Edit | Submit | Display | NoForm
= Collect | NoCollect
18
A very small *complete* example IV
simple :: (Task Person)
simple = editTask "Done" createDefault
19
A very small *complete* example IV Submit
simple :: (Task Person)
simple = editTask "Done" createDefault <<@ Submit
20
A very small *complete* example IV, Submit, Database
simple :: (Task Person)
simple = editTask "Done" createDefault <<@ Submit <<@ Database
21
A very small *complete* example IV, Submit, TxtFile
simple :: (Task Person)
simple = editTask "Done" createDefault <<@ Submit <<@ TxtFile
22
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
23
Sequencing of tasks
Sequencing / composition of tasks (monadic style):
(=>>) infix 1
(#>>) infixl 1
:: (Task a) (a Task b)
:: (Task a) (Task b)
Task b
Task b
Returning plain values as a Task value
return_V
:: a
Task a
| iData a
24
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
(!>>) infix 5
:: [BodyTag] (Task a)
:: [BodyTag] (Task a)
Task a
Task a
| iData a
| iData a
25
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
| Abbr
| Acronym
| Address
| Applet
| Area
|B
…
| Txt
|U
| Ul
| Var
[A_Attr] [BodyTag]
[Std_Attr] String
[Std_Attr] String
[Std_Attr] String
[Applet_Attr] String
[Area_Attr]
[Std_Attr] String
//
//
//
//
//
//
//
link ancor <a></a>
abbreviation <abbr></abbr>
acronym <acronym></acronym>
address <address></address>
applet <applet></applet>
link area in an image <area>
bold <b></b>
String
[Std_Attr] String
[Ul_Attr] [BodyTag]
[Std_Attr] String
//
//
//
//
plain text
underlined text <u></u>
unordered list <ul></ul>
variable text <var></var>
26
Sequence of iTasks
sumInt :: (Task Int)
sumInt
=
editTask "Done" createDefault
=>> \v1
editTask "Done" createDefault
=>> \v2
[Txt "+", Hr [] ]
!>> return_D (v1 + v2)
27
Simple Coffeemachine (1/3)
28
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_V product]
29
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_V product)
30
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)
orTasks
:: [(String,Task a)]
(Task a)
| iData a
| iData a
And task, do all in any order, and finish when all completed
(-&&-) infixr 4
:: (Task a) (Task b)
(Task (a,b))
andTasks
:: [(String,Task a)]
(Task [a])
| iData a & iData b
| iData a
Treat user defined function as a new task: enables recursion
newTask
:: String (Task a)
(Task a)
| iData a
31
Coffeemachine (1/3)
32
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))
33
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 ]
34
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
35
Multi-user combinators
Multi-user :
(@:) infix 3
(@::) infix 3
:: (String, Int) (Task a)
:: Int (Task a)
(Task a)
(Task a)
| iData a
| iData a
36
Multi-User example: Review Task
37
Multi-User example: Review Task
38
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_V Approved)
, ("Reject",
return_V Rejected)
]
39
Review Task (2/3)
taskToReview :: Int (a, a Task a) Task (a, Review) | iData a
taskToReview reviewer (val, task)
= newTask "taskToReview" taskToReview`
where
taskToReview`
=
task val
=>> \newval
reviewer @:: reviewTask newval
=>> \review
[Txt ("Reviewer " <+++ reviewer <+++ " says ") , toHtml review, Br]
?>> editTask "OK" Void
#>>
case review of
(NeedsRework _) taskToReview reviewer (newval, task)
else
return_V (newval, review)
40
Review Task (3/3)
:: QForm =
{ toComp
, startDate
, endDate
, estimatedHours
, description
, price
}
:: String
:: HtmlDate
:: HtmlDate
:: Int
:: TextArea
:: Real
startTask :: Task (QForm, Review)
startTask = taskToReview 1 (createDefault, mytask)
mytask :: a (Task a) | iData a
mytask v = [Txt "Fill in Form:", Br, Br]
?>> editTask "TaskDone" v <<@ Submit
41
Higher-Order Tasks
Tasks not only deliver values, they may deliver a task under development !
:: TClosure a
:: Maybe a
= TClosure (Task 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
42
Multi-User example: delegate a task
43
Delegate a Task
delegateToSomeone :: Int (Task a) [Int] (Task a) | iData a
delegateToSomeone me task set = newTask "delegateToSomeone" doDelegate
where
doDelegate
=
orTasks [ ("Waiting for " <+++ who
, who @:: chooseTask [("I Will Do It“, return_V who)]
) \\ who set
]
=>> \volunteer volunteer @:: stopIt -!> task
=>> \(stopped, TClosure task)
if (isJust stopped) (delegateToSomeone me task set) task
stopIt
= stop -||- (me @:: stop)
stop
= chooseTask [("Stop“, return_V True)]
44
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
45
Login handling types and functions …
:: Accounts s
:: Account s =
:: Maybe a
:== [Account s]
{ login
, uniqueId
, state
}
{ loginName
, password
}
= Just a | Nothing
addAccount
removeAccount
changeAccount
:: (Account s) (Accounts s) (Accounts s)
:: (Account s) (Accounts s) (Accounts s)
:: (Account s) (Accounts s) (Accounts s)
hasAccount
:: Login
invariantLogins
:: String [Login] Maybe (String,String)
:: Login =
:: Login
:: Int
:: s
// login info
// unique identifier
// state
:: String
:: PasswordBox
// Should be unique
// Should remain secret
(Accounts s) (Maybe (Account s))
46
iTasks can be used for persistent storage of information
definition module iTaskDB
import iTasks
:: DBid a
mkDBid :: String Lifespan (DBid a)
readDB :: (DBid a)
writeDB :: (DBid a) a
Task a
Task a
| iData a
| iData a
47
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
addAccountsDB acc accs = writeDB accountId (addAccount acc accs)
48
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
addAccountsDB acc accs = writeDB accountId (addAccount acc accs)
49
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))
addAccountsDB acc accs = writeDB accountId (addAccount acc accs)
50
Handling login's (1)
handleLogin :: (Task (Maybe (Account Void)))
handleLogin
=
[Txt "Type in your name and password...", Br, Br]
?>> loginForm
=>> \login
readAccountsDB
=>> \accounts
return_V (hasAccount login accounts)
loginForm :: (Task Login)
loginForm = editTask "Done" createDefault <<@ Submit
51
Handling login's (2)
newLogin :: (Task (Account Void))
newLogin = newTask "newLogin" newLogin`
newLogin`
=
=>> \login
=>> \accounts
[Br, Br, Txt "Type in name and password you want to use...", Br ,Br]
?>> loginForm
readAccountsDB
case (invariantLogins "" [login:[account.login \\ account <- accounts]]) of
(Just (_,error)) [Txt error, Br, Br]
?>> newLogin
Nothing
let
newId = length accounts
newAccount =
{ login
= login
, uniqueId = newId
, state = Void
}
in
addAccountsDB newAccount accounts
=>> \_ [Txt ("You are administrated
, your id = " <+++ newId)]
?>> buttonTask "OK" (return_V newAccount)
52
Handling login's (3)
loginProcedure :: (Task Int)
loginProcedure = newTask "loginProcedure" loginProcedure`
loginProcedure`
=
chooseTask [ ("Login",
, ("New Login",
=>> \account ->
handleLogin)
newLogin
return_V (Just account))
]
=>> \mbacc ->
-||buttonTask "Cancel" (return_V Nothing)
case mbacc of
Nothing
[Txt "Sorry, you have to try again!",Br,Br]
?>> buttonTask "OK" loginProcedure
(Just acc)
return_V acc.uniqueId
53
Defining a multi-user workflow system (1)
Start world = doHtmlServer (singleUserTask -1 True myAppl ) world
myAppl
=
=>> \myid
=>> \accounts
loginProcedure
readAccountsDB
startNewTask myid True
(assignTasks accounts <<@ TxtFile)
assignTasks accounts
= andTasks [
( acc.login.loginName
, acc.uniqueId @:: assignWork acc.login.loginName acc.uniqueId
)
\\ acc <- accounts
]
assignWork name i = …
54
Defining a multi-user workflow system (2)
assignWork ::
Bool
(Task Void)
(acc Task acc)
((String,Int,acc) (Task a))
(Task [a]) | iData acc & iData
//
//
//
//
a
traceOn
welcome task
administration task
\name uniqueid admin ->
Examples:
newsgroups
marking
55
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
56
Generic programming
Some functions are more or less the same:
equality, unification, mapping, zipping, folding,
pretty printers, parsers, generators,
GAST: automatic test system
Graphical User Interfaces
???
Define the general case once and for all
by induction on the structure of data types.
Automatically obtain a concrete function for any concrete data type !
Applicable for quite a large class of algorithms…
Exceptions make the rule !
One can define specialized instances for special types…
57
Example types
:: List a
= Nil
| Cons a (List a)
:: Rose a
= Rose a (List (Rose a))
:: Tree a b
= Tip a
| Bin b (Tree a b) (Tree a b)
58
Overloaded equality
class == infix 4 t :: t t Bool
instance == (List a) | == a
where (==) Nil
Nil
(==) (Cons x xs) (Cons y ys)
(==) _
_
= True
= x == y && xs == ys
= False
instance == (Rose a) | == a
where (==) (Rose x xs) (Rose y ys)
(==) _
_
= x == y && xs == ys
= False
instance == (Tree a b) | == a & == b
where (==) (Tip x)
(Tip y)
= x == y
(==) (Bin x ltx rtx) (Bin y lty rty) = x == y && ltx == lty && rtx == rty
(==) _
_
= False
59
The idea of generic programming
Map_List
List a
List b
Map_Rose
Rose a
Rose b
60
The idea of generic programming
Map_List
List a
List b
Map_Generic
Generic a
Generic b
Map_Rose
Rose a
Rose b
61
The idea of generic programming
Map_List
List a
List b
fromList
toList
Map_Generic
Generic a
Generic b
fromRose
toRose
Map_Rose
Rose a
Rose b
62
Generic programming
We need a generic representation:
a way to represent any value of any type
Clean is typed: what is the type of such a representation ?
One generic type which can represent all possible types not possible:
type correctness of programs becomes undecidable
one would obtain a complete different system
Solution: use a couple of simple types to represent any type !
63
Generic type representation
Example types
:: List a
:: Rose a
:: Tree a b
= Nil | Cons a (List a)
= Rose a (List (Rose a))
= Tip a | Bin b (Tree a b) (Tree a b)
Binary sums and products (in generic prelude)
:: UNIT
= UNIT
:: PAIR a b
= PAIR a b
:: EITHER a b
= LEFT a | RIGHT b
Generic type representations
:: ListG a
:== EITHER UNIT (PAIR a (List a))
:: RoseG a
:== PAIR a (List (Rose a))
:: TreeG a b
:== EITHER a (PAIR b (PAIR (Tree a b) (Tree a b)))
64
Generic type representation
Conversions to the generic domain can be automatically generated
fromList :: (List a) ListG a
fromList Nil
= LEFT UNIT
fromList (Cons a as)
= RIGHT (PAIR a as)
fromRose :: (Rose a) RoseG a
fromRose (Rose a list_of_roses) = PAIR a list_of_roses
fromTree :: (Tree a b) TreeG a b
fromTree (Tip a) = LEFT a
fromTree (Bin b leftTree rightTree) = RIGHT (PAIR b (PAIR leftTree rightTree))
65
Generic type representation
Conversions back to the user domain can be automatically generated
toList :: (ListG a) List a
toList LEFT UNIT
toList (RIGHT (PAIR a as))
= Nil
= Cons a as
toRose :: (RoseG a) Rose a
toRose (PAIR a list_of_roses)
= Rose a list_of_roses
toTree :: (TreeG a b) Tree a b
toTree (LEFT a)
= Tip a
toTree (RIGHT (PAIR b (PAIR leftTree rightTree))) = Bin b leftTree rightTree
66
Generic equality
generic gEq a :: a a Bool
gEq
gEq
gEq
gEq
gEq
{|Int|} x y
{|Char|} x y
{|Bool|} x y
{|Real|} x y
{|String|} x y
gEq {|UNIT|} UNIT UNIT
=x
=x
=x
=x
=x
== y
== y
== y
== y
== y
= True
gEq {|PAIR|} eqx eqy (PAIR x1 y1) (PAIR x2 y2) = eqx x1 x2 && eqy y1 y2
gEq {|EITHER|} eqx eqy (LEFT x) (LEFT y)
= eqx x y
gEq {|EITHER|} eqx eqy (RIGHT x) (RIGHT y) = eqy x y
gEq {|EITHER|} eqx eqy
__
= False
gEq {|MyType|} …
=
67
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
68
editTask uses generic functions
class
class
class
class
class
class
iData a
iCreateAndPrint a
iCreate a
iPrint a
iParse a
iSpecialStore a
|
|
|
|
|
|
gForm {|*|} , iCreateAndPrint, iParse, iSpecialStore a
iCreate, iPrint a
gUpd {|*|} a
gPrint {|*|} a
gParse {|*|} a
gerda {|*|}, TC a
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 !
69
Implementation architecture
v :: T
Clean Application
70
Implementation architecture
Browser
Html code
Web Server
gForm: Html code
v :: T
Clean Application
serialize: gPrint
store: gerda
File / Data Base
71
Implementation architecture
Browser
Web Server
File / Data Base
72
Implementation architecture
Browser
∆v :: T∆v
Web Server
∆v :: T∆v
gUpd: v` :: T
Clean Application
v :: T
File / Data Base
de-serialize: gParse
retrieve: gerda
73
Implementation architecture
Browser
Html code
∆v :: T∆v
Web Server
gForm: Html code
v :: T
∆v :: T∆v
gUpd: v` :: T
Clean Application
v :: T
serialize: gPrint
store: gerda
File / Data Base
de-serialize: gParse
retrieve: gerda
74
Generic functions used for i-Tasks / i-Data
The following generic functions are used:
generic gForm a :: (Init, FormId a) *HSt *(Form a, *HSt)
can create an interactive Html form for any value of any type
generic gUpd a :: UpdMode a (UpdMode, a)
can update a value of any type given any change made in a form
generic gPrint a :: a String
can serialize a value of any (first-order) Clean type
to store a form state in a page, file or database
generic gParse a :: String Maybe a
can de-serialize a value of any (first-order) Clean type
to re-construct a form state
75
Generating forms
generic gForm a :: (Init, FormId a)
:: FormId a
=
{ id
, initval
, mode
, lifespan
, storage
}
:: String
:: a
:: Mode
:: Lifespan
:: StorageFormat
*HSt *(Form a, *HSt)
//
//
//
//
//
id *uniquely* identifying the form
initial value (Init) or new value (Set)
kind of form
where to store it, and for how long
storage format
76
Generating forms
generic gForm a :: (Init, FormId a)
*HSt *(Form a, *HSt)
:: FormId a
=
{ id
, initval
, mode
, lifespan
, storage
}
:: String
:: a
:: Mode
:: Lifespan
:: StorageFormat
:: Init
:: Mode
:: Lifespan
:: StorageFormat
= Init | Set
= Edit | Submit | Display | NoForm
= Database | TxtFile | Session | Page | Temp
= StaticDynamic | PlainString
//
//
//
//
//
id *uniquely* identifying the form
initial value (Init) or new value (Set)
kind of form
where to store it, and for how long
storage format
77
Generating forms
generic gForm a :: (Init, FormId a)
*HSt *(Form a, *HSt)
:: FormId a
=
{ id
, initval
, mode
, lifespan
, storage
}
:: String
:: a
:: Mode
:: Lifespan
:: StorageFormat
:: Init
:: Mode
:: Lifespan
:: StorageFormat
:: *HSt
= Init | Set
= Edit | Submit | Display | NoForm
= Database | TxtFile | Session | Page | Temp
= StaticDynamic | PlainString
// State passed around single threadedly
//
//
//
//
//
id *uniquely* identifying the form
initial value (Init) or new value (Set)
kind of form
where to store it, and for how long
storage format
78
Generating forms
generic gForm a :: (Init, FormId a)
*HSt *(Form a, *HSt)
:: FormId a
=
{ id
, initval
, mode
, lifespan
, storage
}
:: String
:: a
:: Mode
:: Lifespan
:: StorageFormat
:: Init
:: Mode
:: Lifespan
:: StorageFormat
:: *HSt
= Init | Set
= Edit | Submit | Display | NoForm
= Database | TxtFile | Session | Page | Temp
= StaticDynamic | PlainString
// State passed around single threadedly
:: Form a
=
{ changed
, value
, form
}
:: Bool
:: a
:: [BodyTag]
//
//
//
//
//
id *uniquely* identifying the form
initial value (Init) or new value (Set)
kind of form
where to store it, and for how long
storage format
// has the user edited the form ?
// current value in data domain (feel)
// html code representing view domain (look)
79
Updating a value (1)
How can we reconstruct a value that has been changed interactively ?
We only need to know 3 things:
Which type was it and what was its original value?
Which parts have been changed in the original value?
»
»
»
We don't need any knowledge about the html code !
We only need to know which values have been changed
We just need the position in its generic representation
What are the changed values ?
Simple case: only a few basic cases:
»
»
Change existing value
Create new values
: Int, Real, String, Bool, …
: When you select a constructor
Complication : change of form may depend on the change of others
80
Updating a value (2)
generic gUpd a
:: UpdMode a (UpdMode,a)
:: UpdMode
= UpdSearch UpdValue Pos
| UpdCreate [ConsPos]
| UpdDone
:: UpdValue
= UpdI Int
| UpdR Real
| UpdB Bool
| UpdS String
| UpdC String
:: ConsPos
= ConsLeft
| ConsRight
:: Pos
// search for position
// in generic representation
// copy the remaining expression
//
//
//
//
//
new Integer value
new Real value
new Boolean value
new String value
new Constructor value
:== Int
81
Updating a value (3)
gUpd {|Int|} (UpdSearch (UpdI ni) 0) _
= (UpdDone,ni)
gUpd {|Int|} (UpdSearch val cnt)
= (UpdSearch val (cnt - 1), i)
i
gUpd {|Int|} (UpdCreate p)
gUpd {|Int|} mode
_
i
= (UpdCreate p, 0)
= (mode, i)
82
Snapshot from Clean's CD shop
83
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
84
What do we need for iTasks ?
We need a notion of “tasks”: inactive, active, and completed
There is only one application for all users
This application starts from scratch each time information is communicated
The web has no notion of state, we have to store all relevant information
We have to be able to find out what we were doing -> functions + iData
We need to invent unique id’s for a form and its corresponding state
Careful: tasks are dynamically created, can be recursive !
We have to find out which tasks are active and deal with them
We have to support all options
If active tasks are completed, we have to activate the next set of tasks
We have to display the tasks in an understandable way
We have to find out who has to do what, and show relevant information only
We have to deal with multi-user aspects
We have to gather trace information
We have to remove unneeded administration of completed tasks
85
iTask Administration State
:: *TSt
=
{ tasknr
, activated
, userId
, currentUserId
, html
, options
, hst
, trace
}
:: [Int]
:: Bool
:: Int
:: Int
:: HtmlTree
:: Options
:: *HSt
:: Maybe [Trace]
//
//
//
//
//
//
//
//
unique form-id for every task
if assigned activate, if returned completed
id of user who has to do the task
id of current application user
accumulator for html code
iData and iTasks options
iData state
for displaying task trace
86
iTask Administration State
:: *TSt
=
{ tasknr
, activated
, userId
, currentUserId
, html
, options
, hst
, trace
}
:: HtmlTree
:: [Int]
:: Bool
:: Int
:: Int
:: HtmlTree
:: Options
:: *HSt
:: Maybe [Trace]
//
//
//
//
//
//
//
//
unique form-id for every task
if assigned activate, if returned completed
id of user who has to do the task
id of current application user
accumulator for html code
iData and iTasks options
iData state
for displaying task trace
= BT [BodyTag]
//
| (@@:) infix 0 (Int,String) HtmlTree//
| (-@:) infix 0 Int HtmlTree
//
| (+-+) infixl 1 HtmlTree HtmlTree //
| (+|+) infixl 1 HtmlTree HtmlTree //
html code
code with user id and task name
code not to display to user id
place code next to each other
place code below each other
87
iTask Administration State
:: *TSt
=
{ tasknr
, activated
, userId
, currentUserId
, html
, options
, hst
, trace
}
:: HtmlTree
:: Options
=
{ tasklife
, taskstorage
, taskmode
, gc
}
:: [Int]
:: Bool
:: Int
:: Int
:: HtmlTree
:: Options
:: *HSt
:: Maybe [Trace]
//
//
//
//
//
//
//
//
unique form-id for every task
if assigned activate, if returned completed
id of user who has to do the task
id of current application user
accumulator for html code
iData and iTasks options
iData state
for displaying task trace
= BT [BodyTag]
//
| (@@:) infix 0 (Int,String) HtmlTree//
| (-@:) infix 0 Int HtmlTree
//
| (+-+) infixl 1 HtmlTree HtmlTree //
| (+|+) infixl 1 HtmlTree HtmlTree //
:: Lifespan
:: StorageFormat
:: Mode
:: GarbageCollect
//
//
//
//
html code
code with user id and task name
code not to display to user id
place code next to each other
place code below each other
default:
default:
default:
default:
Session
PlainString
Edit
Collect
88
Activating a task (simplified)
:: Task a
:== *TSt *(a,*TSt)
89
Activating a task (simplified)
:: Task a
:== *TSt *(a,*TSt)
Each task is assumed to call doTask
increment task number, such that this number is unique;
when activated call the task, create a default value otherwise;
when a task is finished, activate is returned True, False otherwise.
90
Activating a task (simplified)
:: Task a
:== *TSt *(a,*TSt)
Each task is assumed to call doTask
increment task number, such that this number is unique;
when activated call the task, create a default value otherwise;
when a task is finished, activate is returned True, False otherwise.
doTask :: String (Task a) (Task a) | iCreateAndPrint a
doTask taskname thistask = mkTaskNoInc taskname thistask o incTaskNr
where
mkTaskNoInc taskname thistask tst=:{activated, tasknr, options}
| not activated
= (createDefault, tst)
# (val,tst)
= thistask tst
= (val, {tst & tasknr = tasknr, options = options})
incTaskNr tst
where
incNr []
incNr [i:is]
= {tst & tasknr = incNr tst.tasknr}
= [0]
= [i+1:is]
91
The bind is now easy... a standard bind can be used
(=>>) infix 1 :: (Task a) (a Task b) Task b
(=>>) taska taskb = bind
where
bind tst
# (a, tst)
= taska tst
= taskb a tst
(#>>) infix 1 :: (Task a) (Task b) Task b
(#>>) taska taskb = bind
where
bind tst
# (_, tst)
= taska tst
= taskb tst
92
A simple combinator: ?>>
(?>>) infix 5 :: [BodyTag] (Task a) -> (Task a) | iCreate a
(?>>) prompt thistask = myTask
where
myTask tst=:{html, activated}
| not activated
= (createDefault, tst)
# (a, tst=:{activated, html = nhtml})
= thistask {tst & html = BT []}
| activated
= (a, {tst & html = html})
= (a, {tst & html = html +|+ BT prompt +|+ nhtml})
93
A simple combinator: -&&(-&&-) infixr 4 :: (Task a) (Task b) (Task (a,b)) | iCreateAndPrint a & iCreateAndPrint b
(-&&-) taska taskb = mkTask "-&&-" (doAndTask (taska, taskb))
doAndTask (taska, taskb) tst=:{tasknr, html}
# (a, tst=:{activated = adone, html = ahtml})
= mkParSubTask "andTask" 0 taska {tst & html = BT []}
# (b, tst=:{activated = bdone, html = bhtml})
= mkParSubTask "andTask" 1 taskb {tst & tasknr = tasknr, html = BT []}
= ((a,b), {tst & activated = adone && bdone, html = html +|+ ahtml +|+ bhtml})
94
A simple combinator: -&&(-&&-) infixr 4 :: (Task a) (Task b) (Task (a,b)) | iCreateAndPrint a & iCreateAndPrint b
(-&&-) taska taskb = mkTask "-&&-" (doAndTask (taska, taskb))
doAndTask (taska, taskb) tst=:{tasknr, html}
# (a, tst=:{activated = adone, html = ahtml})
= mkParSubTask "andTask" 0 taska {tst & html = BT []}
# (b, tst=:{activated = bdone, html = bhtml})
= mkParSubTask "andTask" 1 taskb {tst & tasknr = tasknr, html = BT []}
= ((a,b), {tst & activated = adone && bdone, html = html +|+ ahtml +|+ bhtml})
mkParSubTask name i task tst = task (newSubTaskNr (setActivated (subTaskNr i tst)))
subTaskNr
newSubTaskNr
setActivated
i tst
tst
tst
= { tst & tasknr = [ i : tst.tasknr] }
= { tst & tasknr = [-1 : tst.tasknr] }
= { tst & activated = True }
95
Clean
Introduction
Defining Interactive Multi-user Workflow Systems for the web
Implementation
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
Basic idea of generic programming
Generic functions for handling web forms: i-Data
Implementation of i-Tasks
Conclusion & Future Research
96
Conclusions
Compact specification, many details are handled automatically
very intuitive
All "standard" workflows patterns offered
but we are lacking a graphical editor...
Offers a lot of additional expressive power
Typed
Workflows are dynamically generated
Additional Workflow Patterns: lazy send / receive, -!>
Higher Order Workflows
Highly re-usable code
First industrial real-world application has been made
(Car Damage Negotiation System, ABZ)
97
Future Work
Show applicability via more, large real world applications
Combine with Client site evaluation
Fast Clean interpreter (Jan-Martin Jansen) running in browser
add “Ajax” technology
Integrate with main stream web development tools (Dreamweaver)
Improve performance
Practical issues: What is the ideal set of combinators ?
Can we exploit lazy evaluation ?
Theoretical issues: What is the minimal set of combinators ?
How does it compare with other approaches: Petri Nets, Process Algebra, μCRL ?
Can we prove properties of a workflow system ?
What kind of static properties can we analyse ?
98