550 likes | 653 Views
T ask O riented P rogramming i n using Rinus Plasmeijer – Bas Lijnse - Peter Achten Pieter Koopman - Steffen Michels – Jurriën Stutterheim Jan Martin Jansen (NLDA) - Laszlo Domoszlai (ELTE) Part 2/2 Radboud University Nijmegen. Overview.
E N D
Task Oriented Programming in using Rinus Plasmeijer–Bas Lijnse- Peter Achten Pieter Koopman- Steffen Michels–JurriënStutterheim Jan Martin Jansen (NLDA) - Laszlo Domoszlai (ELTE) Part 2/2 Radboud University Nijmegen
Overview • Introduction to Task Oriented Programming • iTask Library • Task Values • Editors • Shared Data • Sequential Tasks • Parallel Tasks • Semantics • Conclusions and Future Work
Embedded Domain Specific Language iTaskCore Basic tasks Interactive Tasks : Editors Non-interactive Tasks: Lifting & Conversion Integrated Tasks: web-service-call, OS-call, exec application, access a database Combinators for combining tasks Define control flow (sequential / parallel) and data flow between tasks + iTaskLibrary to support frequently occurring work patterns + Clean: general purpose pure functional programming language Recursive -, higher order -, polymorphic -, overloaded -, generic - functions Hybrid typing: strongly statically typed + dynamic typing
Core Concepts • Data • Local data: Task Values • Shared data: Shared Data Sources • Tasks • Basic Tasks • Non-interactive • return, throw, … • Interactive • Editors • Combinators • Sequential • Parallel
Core - Task Values :: Task atyped unit of work which should deliver a value of type a While the task is going on, its value may change over time :: TaskResult a = ValResTimeStamp (Value a) | e: ExcRese & iTask e :: Value a = NoVal | Vala Stability :: Stability = Unstable | Stable NoVal Val a Unstable Vala Stable
Interactive Editors (see InteractionTasks.dcl) Basic tasks: Interactive editor for filling in forms of a certain type: viewInformation :: d [ViewOption a] a Task a | descr d & iTask a enterInformation :: d [EnterOption a] Task a | descr d & iTask a updateInformation:: d [UpdateOption a a] a Task a | descr d & iTask a enterChoice :: d [ChoiceOptiono] [o] Task o | descr d & iTask o updateChoice :: d [ChoiceOptiono] [o] o Task o | descr d & iTask o enterMultipleChoice :: d [MultiChoiceOptiono] [o] Task [o] | descr d & iTask o updateMultipleChoice :: d [MultiChoiceOptiono] [o] [o] Task [o] | descr d & iTask o
Overview • Introduction to Task Oriented Programming • iTask Library • Task Values • Editors • Shared Data • Sequential Tasks • Parallel Tasks • Semantics • Conclusions and Future Work
Core - Shared Data Sources SDS: one abstraction layer for any type of shared data: easy to use for the progammer - Shared Memory , Files , Cloud , Time , Sensors , …. - Reading and Writing can be of different type - SDS’s can be composed from others (e.g. lenses a la Benjamin Pierce) :: RWShared r w :: Shared a :== RWShared a a ::ReadOnlyShared a :==RWShareda Void ::WriteOnlyShared a :==RWSharedVoid a
Shared Data Sources Creating an SDS: withShared :: a ((Shared a) →Task b) →Task b | iTask b // Shared memory sharedStore:: String a→ Shared a | iTask a // Special File externalFile :: FilePath→Shared String // Ordinary File sqlShare :: SQLDatabase String … →ReadWriteShared r w// SQL Database Reading an SDS: get :: (RWShared r w) →Task r | iTask r // read once currentTime::ReadOnlyShared Time currentDate::ReadOnlySharedDate currentDateTime::ReadOnlySharedDateTime currentUser::ReadOnlyShared User users :: ReadOnlyShared [User] Updating an SDS: set :: w (RWShared r w) →Task w | iTask w // write once update ::(r → w) (RWShared r w)→ Task w | iTask r & iTask w
Interactive Editors on SDS’s viewSharedInformation:: d [ViewOption r] (RWSharedr w) Task r | descr d & iTask r updateSharedInformation :: d [UpdateOption r w] (RWSharedr w) Task w | descr d & iTask r & iTask w enterSharedChoice :: d [ChoiceOption o] (RWShared[o] w) Task o | descr d & iTask o & iTask w updateSharedChoice :: d [ChoiceOption o] (RWShared[o] w) o Task o | descr d & iTask o & iTask w enterSharedMultipleChoice :: d [MultiChoiceOption o] (RWShared[o] w) Task [o] | descr d & iTask o & iTask w updateSharedMultipleChoice :: d [MultiChoiceOption o] (RWShared[o] w) [o] Task [o] | descr d & iTask o & iTask w
Editors on SDS’s viewCurDateTime:: Task DateTime viewCurDateTime = viewSharedInformation"The current date and time is:" [] currentDateTime
Editors on SDS’s twoTasks:: a → Task a | iTask a twoTasksv = withShared v doTasks doTasks:: (Shared a)→ Task a | iTask a doTaskssv = user1 @:updateSharedInformationsv -|| user2 @: viewSharedInformationsv Assign task to someone do both tasks in parallel, return first
Core - Basic Tasks : One editor for all cases • Basic Tasks / Editor edit :: d l (ROShared r) (r→ (l,v)) (l r v→(l,v))→Task l | descr d & iTaskl & iTask r & iTask v • One editor for handling all editor variants in the iTasks API • An editable viewv is created given the local valuel and shared valuer • The view v is updated when v is edited or someone changes r • An editor task never delivers a stable value (it is either NoVal or VallUnstable) • Tasks do not terminate, but are discarded if they are not needed anymore
Overview • Introduction to Task Oriented Programming • iTask Library • Task Values • Editors • Shared Data • Sequential Tasks • Parallel Tasks • Semantics • Conclusions and Future Work
Sequential Combinator: >>* palindrome :: Task (Maybe String) palindrome = enterInformation "Enter a palindrome" [] >>* [ OnActionActionOk (ifValueisPalindrome (\v return (Just v))) , OnActionActionCancel (always (returnNothing)) ]
Sequential Combinator: >>* >>* • Observe Task a, continue with one of the Task b's: - if a certain action is performed by the end-user - if the value of the observed task is satisfying a certain predicate - or the observed task has raised an exception to be handled here Task b Task b Task b Task a
Core – Sequential Combinator • Combinator for Sequential Composition (>>*) infixl 1 :: (Task a) [TaskStep a b] →Task b | iTask a & iTask b :: TaskStep a b = OnActionAction(Value a→Maybe(Task b)) | OnValue(Value a→Maybe (Task b)) | E.e: OnException (e→Task b) & iTask e :: Action = ActionString[ActionOption] :: ActionOption = ActionKey Hotkey | ActionWeightInt | ActionIcon String |ActionTriggerDoubleClick :: Hotkey = { key :: Key, ctrl :: Bool, alt :: Bool, shift :: Bool} ActionOk :== Action "Ok“ [ActionIcon"ok", ActionKey(unmodified KEY_ENTER)]
Core – Sequential Combinator • Combinator for Sequential Composition (>>*) infixl 1 :: (Task a) [TaskStep a b] →Task b | iTask a & iTask b :: TaskStep a b = OnActionAction(Value a→Maybe(Task b)) | OnValue(Value a→Maybe (Task b)) | E.e: OnException (e→Task b) & iTask e :: Action = ActionString [ActionOption] :: ActionOption = ActionKeyHotkey | ActionWeightInt | ActionIconString |ActionTriggerDoubleClick :: Hotkey = { key :: Key, ctrl :: Bool, alt :: Bool, shift :: Bool} ActionOpen:== Action "/File/Open" [ActionIcon"open", ActionKey(ctrlKEY_O)]
Core – Sequential Combinator • Combinator for Sequential Composition (>>*) infixl 1 :: (Task a) [TaskStep a b] →Task b | iTask a & iTask b :: TaskStep a b = OnActionAction(Value a→Maybe(Task b)) | OnValue(Value a→Maybe (Task b)) | E.e: OnException (e→Task b) & iTask e alwaystask = const(Justtask) ifValuepredtask (Value v _) = if (pred v) (Just (task v)) Nothing ifValue_ _ _ = Nothing hasValuetask (Value v _) = Just (task v) hasValue_ _ = Nothing ifStabletask(Valuev stable) = ifstable (Just (task v)) Nothing ifStable_ _ = Nothing
Recursive Tasks person1by1 :: [Person] Task [Person] person1by1 persons = enterInformation "Add a person" [] >>* [ OnAction (Action "Add" []) (hasValue(\vperson1by1 [v : persons]) , OnAction (Action "Finish" []) (always (return persons)) , OnActionActionCancel (always (return[])) ]
Recursive Tasks person1by1 :: [Person] Task [Person] person1by1 persons = enterInformation "Add a person" [] -|| viewInformation "List so far.." [] persons >>* [ OnAction (Action "Add" []) (hasValue(\vperson1by1 [v : persons]) , OnAction (Action "Finish" []) (always (return persons)) , OnActionActionCancel (always (return[])) ]
Monadic-style Monadic style: (>>=) infix 1 :: (Task a) (a Task b) Task b | iTask a & iTask b return :: a Task a | iTask a (>>=) infix 1 :: (Task a) (a Task b) Task b | iTask a & iTask b (>>=) taska taskbf = taska >>* [OnActionActionContinue (hasValue taskbf) , OnValue (ifStable taskbf) ]
Simple Sum calculateSum :: Task Int calculateSum = enterInformation ("Number 1","Enter a number") [] >>= \num1 enterInformation ("Number 2","Enter another number") [] >>= \num2 viewInformation ("Sum","The sum of those numbers is:") [] (num1 + num2)
Overview • Introduction to Task Oriented Programming • iTask Library • Task Values • Editors • Shared Data • Sequential Tasks • Parallel Tasks • Semantics • Conclusions and Future Work
Parallel - I parallel :: d [(ParallelTaskType, (ReadOnlyShared (TaskList a)) Task a)] Task [(TaskTime, TaskValue a)] | descr d & iTask a What kind of task is it and who should work on it ? A dedicated process table (TaskList) is created as SDS enabling the parallel tasks to inspect each other Other tasks can see the current values of the parallel tasks Every task produces a value of "type a"
Parallel - I parallel :: d [(ParallelTaskType, (ReadOnlyShared (TaskList a)) Task a)] Task [(TaskTime, TaskValue a)] | descr d & iTask a :: ParallelTaskType = Embedded | DetachedManagementMeta :: ManagementMeta = { title :: Maybe String , worker :: UserConstraint , role :: Maybe Role , startAt :: Maybe DateTime , completeBefore :: Maybe DateTime , notifyAt :: Maybe DateTime , priority :: TaskPriority } :: UserConstraint = AnyUser | UserWithIdUserId | UserWithRoleRole
Parallel - II parallel :: d [(ParallelTaskType, (ReadOnlyShared (TaskList a)) Task a)] Task [(TaskTime, TaskValue a)] | descr d & iTask a :: TaskList a = { listId :: TaskListId a , items :: [TaskListItem a] } :: TaskListItem a = { taskId :: TaskId , value :: TaskValue a , taskMeta :: TaskMeta , managementMeta :: Maybe ManagementMeta , progressMeta :: Maybe ProgressMeta }
Parallel Task Handling parallel :: d [(ParallelTaskType, (ReadOnlyShared (TaskList a)) Task a)] Task [(TaskTime, TaskValue a)] | descr d & iTask a appendTask :: ParallelTaskType ((ReadOnlyShared (TaskList a)) Task a) (ReadOnlyShared (TaskList a)) Task TaskId | iTask a removeTask :: TaskId(ReadOnlyShared (TaskList a)) Task Void | iTask a Only the parallel tasks have access to their process table and they can add new tasks or kill existing ones
Handy predefined functions based on parallel and : return values of all (embedded) parallel tasks: allTasks :: [Task a] Task [a] | iTask a (-&&-) infixr 4 :: (Task a) (Task b) Task (a, b) | iTask a & iTask b or: return result of (embedded) parallel tasks yielding a value as first: eitherTask :: (Task a) (Task b) Task (Either a b) | iTask a & iTask b anyTask :: [Task a] Task a | iTask a (-||-) infixr 3 :: (Task a) (Task a) Task a | iTask a one-of: start two tasks, but we are only interested in the result of one of them, use the other to inform: (-||) infixl 3 :: (Task a) (Task b) Task a | iTask a & iTask b (||-) infixr 3 :: (Task a) (Task b) Task b | iTask a & iTask b
Handy predefined functions based on parallel allTasks :: [Task a] Task [a] | iTask a allTasks tasks = parallelVoid [(Embedded, const t) \\ t tasks] @res whereres vs = [v \\ (_,Value v _) vs] (-&&-) infixr 4 :: (Task a) (Task b) Task (a,b) | iTask a & iTask b (-&&-) taska taskb = parallelVoid [(Embedded, \_ taska @Left),(Embedded, \_ taskb @Right)] @? res where res (Value [(_, Value (Left a) _),(_,Value (Right b) _)] s) = Value (a,b) s res _ = NoValue The following conversion functions have already been introduced : • (@?) infixl 1 :: (Task a) (Value a→Value b) →Task b | iTask a & iTask b • (@) infixl 1 :: (Task a) (a→b) →Task b | iTask a & iTask b
Handy predefined functions based on parallel anyTask :: [Task a] Task a | iTask a anyTask tasks = parallelVoid [(Embedded, const t) \\ t tasks] @? res where res (Value l _) = hd ([v \\ (_,v=:(Value _ Stable)) l] ++ [v \\ (_,v=:(Value _ _)) sortBy (\a b -> fst a > fst b) l] ++ [NoValue]) res _ = NoValue (-||) infixl 3 :: (Task a) (Task b) Task a | iTask a & iTask b (-||) taska taskb = parallelVoid [(Embedded, \_ taska @Left),(Embedded, \_ taskb @Right)] @? Res where res (Value [(_,Value (Left a) s),_] _) = Value a s res _ = NoValue
Handy predefined functions based on parallel and : return values of all (embedded) parallel tasks: allTasks :: [Task a] Task [a] | iTask a (-&&-) infixr 4 :: (Task a) (Task b) Task (a, b) | iTask a & iTask b or: return result of (embedded) parallel tasks yielding a value as first: eitherTask :: (Task a) (Task b) Task (Either a b) | iTask a & iTask b anyTask :: [Task a] Task a | iTask a (-||-) infixr 3 :: (Task a) (Task a) Task a | iTask a one-of: start two tasks, but we are only interested in the result of one of them, use the other to inform: (||-) infixr 3 :: (Task a) (Task b) Task b | iTask a & iTask b (-||) infixl 3 :: (Task a) (Task b) Task a | iTask a & iTask b assign a task to a specific user: (@:) infix 3 :: User (Task a) Task a | iTask a
Example: Chat with someone chat :: Task Void chat = getcurrentUser >>= \me enterSharedChoice "Select someone to chat with:" [] users >>= \youwithShared ("","") (duoChatyou me) duoChat you me notes = chat you toViewfromView notes -||- (you @:chat me (toViewoswitch) (\a v switch (fromView a v)) notes) where toView (you, me) = (Display you, Note me) fromView _ (Display you, Note me) = (you, me) switch (you, me) = (me, you) chat who toV fromV notes = updateSharedInformation ("Chat with " <+++ who) [UpdateWith toV fromV] notes >>* [OnAction (Action "Stop") always (returnVoid)]
A Text Editor - I • editWithStatistics :: Task Void • editWithStatistics • = enterInformation "Give name of text file you want to edit..." [] • >>= \fileNamelet file = sharedStorefileName "" • in parallelVoid [ (Embedded, editFilefileName file) • , (Embedded, showStatistics file) • , (Embedded, replaceinitReplace file) • ] • >>* [ OnAction (Action "File/Quit") (always(returnVoid))]
A Text Editor - II editFile :: String (Shared String) (SharedTaskList Void) Task Void editFilefileNamesharedFile _ = updateSharedInformation ("edit " +++ fileName) viewsharedFile@ constVoid where view = [UpdateWithtoVfromV] toV text = Note text fromV _ (Note text) = text
A Text Editor - III :: Statistics = {lineCount:: Int, wordCount:: Int} deriveclassiTaskStatistics showStatisticssharedFile _ = noStat<<@ Window where noStat :: Task Void noStat = viewInformationVoid [] Void >>* [ OnAction (Action "File/Show Statistics") (alwaysshowStat) ] showStat :: Task Void showStat = viewSharedInformation "Statistics:" [ViewWithstat] sharedFile >>* [ OnAction (Action "File/Hide Statistics") (alwaysnoStat) ] stat text = {lineCount= lengthLines text, wordCount= lengthWords text} wherelengthLines "" = 0 lengthLines text = length (split "\n" text) lengthWords "" = 0 lengthWords text = length (split " " (replaceSubString "\n" " " text))
A Text Editor - IV :: Replace = {search :: String, replaceBy :: String} deriveclassiTaskReplace replacecmndsharedFile _ = noReplacecmnd where noReplace :: ReplaceTask Void noReplacecmnd = viewInformationVoid [] Void >>* [ OnAction (Action "File/Replace") (always(showReplacecmnd)) ] showReplace :: ReplaceTask Void showReplacecmnd = updateInformation "Replace:" [] cmnd >>* [ OnAction (Action "Replace") (hasValuesubstitute) , OnAction (Action "Cancel") (always(noReplacecmnd)) ] substitutecmnd = update (replaceSubStringcmnd.searchcmnd.replaceBy) sharedFile >>| showReplacecmnd
Overview • Introduction to Task Oriented Programming • iTask Library • Task Values • Editors • Shared Data • Sequential Tasks • Parallel Tasks • Semantics • Conclusions and Future Work
Semantics The iTask system is quite powerful • tasks are defined on a complex domain and are reactive • multi-user system • shared data structures The core is small • operational semantics described in Clean • readable, concise • type checked by the compiler • executable, one can check its behavior • blueprint for implementations
Core - Task Values • :: Task atyped unit of work which should deliver a value of type a :: TaskResult a = ValResTimeStamp(Value a) | e: ExcRese & iTask e :: Value a = NoVal | Vala Stability :: Stability = Unstable | Stable NoVal Val a Unstable Vala Stable
Semantics - What is a Task ? :: Task a :== Event*State→*((Reduct a, [(TaskNo, Response)]), *State) :: Reduct a = Reduct (TaskResult a) (Task a) :: *State = { taskNo :: TaskNo // highest unassigned number , timeStamp :: TimeStamp // current time stamp , mem :: [SharedValue] // memory shared between tasks , world :: *World // enables I/O in a pure FPL } :: SharedValue :== Dynamic Current Value Remaining Task To do
Semantics - Types :: Event = RefreshEvent | EditEventTaskNo Dynamic | ActionEventTaskNo Action :: Response = EditorResponseEditorResponse | ActionResponseActionResponse :: EditorResponse = { description :: String , editValue :: (LocalVal,SharedVal) , editing :: EditMode } :: LocalVal :== Dynamic :: SharedVal :== Dynamic :: EditMode = Editing | Displaying :: ActionResponse :== [(Action,Bool)] :: Action = ActionString | ActionOk | ActionCancel | …
Semantics -Task Rewriting evaluateTask :: (Task a) *World→ *(Maybe a,*World) | iTask a evaluateTask task world # st = {taskNo = 0, timeStamp = 0, mem = [], world = world} # (ma, st) = rewrite task st = (ma, st.world ) rewrite :: (Task a)*State→ *(Maybe a,*State) | iTask a rewrite task st # (ev, nworld) = getNextEventst.world # (t, nworld) = getCurrentTimenworld # ((Reductresultntask, responses), st) = task ev {st & timeStamp = t, world = nworld} = caseresultof ValRes _ (Val a Stable) → (Just a, st) ExcRes _ → (Nothing, st) _ →rewritentask {st & world = informClients responses st.world}
Semantics -Task Rewriting throw :: e→Task e | iTask e throw e evst = ((Reduct (ExcRes e) (throw e), []), st) return :: a→Task a returnvaevst=:{timeStamp = t} = stable t vaevst where stable t va _ st= (Reduct (ValRes t (Val va Stable)) (stable t va),[],st) (@?) infixl 1 :: (Task a) ((Value a)→Value b) →Task b | iTask a & iTask b (@?) task convevst = case task evstof (Reduct (ValRes t aval) ntask, rsp, nst) →caseconvavalof Val b Stable→return b evnst bval→ ((Reduct (ValRes t bval) (ntask@?conv), rsp), nst) (Reduct (ExcRes e) _,_,nst) →throw e evnst
iTask Core Summary - I • Basic Tasks / Editor edit :: Stringl (RWShared r w) → (lr→Maybe a) →Task a | iTask l & iTask r • Basic Tasks / Return return:: a→Task a | iTask a • Basic Tasks / Exception Handling throw :: e→Task e | iTask e • Shared Data Sources withShared :: a ((Shared a) →Task b) →Task b | iTask a
iTask Core Summary - II • Combinator for Conversion of Task Values (@?) infixl 1 :: (Task a) ((Value a) →Value b) →Task b | iTask a & iTask b • Combinator for Sequential Composition (>>*) infixl 1 :: (Task a) [TaskStep a b]→Task b | iTask a & iTask b :: TaskStep a b = OnActionAction ((Value a) →Bool) ((Value a) →Task b) | OnValue ((Value a) →Bool) ((Value a) →Task b) | E.e: OnException (e→Task b) & iTask e • Combinator for Parallel Composition parallel :: [RWShared [(Int, Reduct a)] Void] →Task [(TimeStamp, Value a)] | iTask a