Workflow examples

From Clean
Jump to navigationJump to search

To determine the direction of the iTasks research we need examples of workflow scenarios to see if they can be implemented in the system.

Basic workflow

Lazy workflow

A lazy workflow is one where a user is not asked to provide any data until it is sure the data are needed for constructing the end result. Well, you can do this in any programming language, so we only consider a workflow to be lazy if not only the computer does a minimum job; also the programmer is allowed to be lazy: a minimum of special language constructs or manual program transformations needed to turn a program from strict into lazy.

We know Clean is lazy in this respect for ordinary program fragments that do not involve I/O. We also know that such laziness in workflows is impossible without compromising referential transparency. But all is not lost.

Dynamic workflow

  • A user is given the task of producing some data (e.g. fill in a large form). Since he does not have all information to complete the task he decides to: fill in part of the form himself, divide the rest of the form and assigns the pieces to a number of experts. Depending on whether the user trusts his experts enough he may setup the task such that: A. the information of the expert is automatically merged with his own and the task is closed and data delivered, or B. he first wants to validate the information from the experts before passing the data along.

Store for anything workflow

  • In this example we construct a store for anything, i.e. the items that you want to sell are determined by their type. The store specification itself is fully overloaded and generic. Stock and orders are stored in a database. The complete source code can be downloaded at
  • We define two workflow situations in this case study.
  • A catalogue management workflow in which a worker can edit, remove, and add items in the store. This is an example of a regular workflow, because each item in stock can be manipulated in the same way. This can be expressed concisely by a map of the stock items.
manageCatalog          :: a [HtmlTag] -> Task a | iData, DB a
manageCatalog _ prompt = stopTask (prompt ?>> foreverTask (dbReadAll =>> browseCatalog))
  browseCatalog        :: [a] -> Task a | iData, DB a
  browseCatalog items  = orTasksVert (map itemActions items ++ [chooseTask_btn [] [("Append",new)]])
     new               = dbCreateItem =>> \first -> editTask "Store" first =>> dbUpdateItem

     itemActions       :: a -> Task a | iData, DB a
     itemActions item  = chooseTask_btn [toHtml item] 
                           [("Edit",   editTask "Store" item =>> dbUpdateItem)
                           ,("Delete", dbDeleteItem (getItemId item) #>> return item)
  • A customer workflow. A customer can browse the shop catalogue, add and remove items in a shopping cart, buy the selected items of the cart, or leave the shop. This is an example of an irregular recursive workflow.
doShopping                 :: (Cart a) [a] -> Task (ShopAction,Cart a) | iData, Product a
doShopping cart []         = (shopPrompt ++ [normalText "Currently no items in catalogue, sorry."])
                             ?>> OK #>> return (LeaveShop,cart)
doShopping cart items      = orTasksVert [ navigateShop shopPrompt cart : map (itemActions cart) items ] 
   itemActions             :: (Cart a) a -> Task (ShopAction,Cart a) | iData, Product a
   itemActions cart item   = chooseTask_btn [toHtml item] [("Add to Cart", return (ToCart, add (toCartItem item) cart))]
      add                  :: (CartItem a) [CartItem a] -> [CartItem a]
      add new []           = [new]
      add new [item:items] = if (eqItemNr new item) 
                                [amountOrderedUpd item (amountOrderedOf item+1):items]
                                [item:add new items]
  • Shop navigation is a matter of selecting the right button:
navigateShop               :: [HtmlTag] (Cart a) -> Task (ShopAction, Cart a) | iData a
navigateShop prompt cart   = chooseTask [] 
                                [ ("Do Shopping",       return (ToShop,   cart))
                                , ("Check Out And Pay", return (ToPay,    cart))
                                , ("Show Cart",         return (ToCart,   cart))
                                , ("Leave Shop",        return (LeaveShop,cart))
                                ] <<? [ BrTag  [], boldText "Total cost of ordered items = ", toHtml (totalCost cart)
                                      , DivTag [] prompt ]
  • The irregular recursive structure is connected together with the following two, mutually recursive, functions:
browseShop                 :: (Cart a) [HtmlTag] [HtmlTag] -> Task Void | iData, DB, Product a
browseShop initCart shopPrompt cartPrompt
                           = dbReadAll      =>> \items ->
                             doShopping initCart items =>> 
                             doAction   initCart items
doAction                   :: (Cart a) [a] (ShopAction, Cart a) -> Task Void | iData, DB, Product a
doAction initCart items (action,cart)
                           = case action of
                               LeaveShop = return Void
                               ToCart    = showCart   cart       =>> doAction initCart items
                               ToShop    = doShopping cart items =>> doAction initCart items
                               ToPay     = checkOutAndPay cart   #>> browseShop initCart shopPrompt cartPrompt
  • Do you want to sell books? Let's make a book type:
:: Book                    = { id_     :: DBRef Book
                             , title   :: String
                             , author  :: String
                             , price   :: HtmlCurrency
                             , inStock :: Int
  • It has to connect to a database:
instance DB Book where databaseId         = mkDBid "books" LSTxtFile
                       getItemId item     = id_Of  item
                       setItemId id item  = id_Upd item id
  • It has to connect to the store, that wants to know what kind of product it is:
class Product a | nameOf, priceOf, id_Of, inStockOf a
instance nameOf    Book where nameOf    r = r.Book.title
instance priceOf   Book where priceOf   r = r.Book.price
instance id_Of     Book where id_Of     r = r.Book.id_
instance inStockOf Book where inStockOf r = r.Book.inStock
  • It has to be the default product and cart content:
defaultProduct             :: Book
defaultProduct             = createDefault
defaultCart                :: Cart Book
defaultCart                = createDefault
  • You're done!