• No se han encontrado resultados

Modificaciones de la respuesta a opiáceos en modelos experimentales de

1.3. ALTERACIONES EN LA RESPUESTA A OPIÁCEOS EN PROCESOS

1.3.2. Modificaciones de la respuesta a opiáceos en modelos experimentales de

pect that when task engineers use frequently occurring function patterns in a consistent way, supported by a coding discipline or tool, it is possible to generate independently understandable blueprints, even for complex applications.

Thirdly, since iTasks is being used to model complex task-collaborations, the dynamic behaviour of the tasks needs to be validated by all stakeholders: What happens when the tasks are executed? Which tasks are executed? Who is doing what? What is the progress? Is the described way of working the best way to achieve the goals? What happens if unexpected tasks need to be done? To answer these questions we need to dynamically relate the statically generated blueprints to the run-time behaviour of their corresponding task function applications during the execution of the iTasks application. This requires a solution akin to debuggers and tracers, which are yet another well-known and hard challenge in functional programming. Fortunately, the iTasks run-time system keeps track of all tasks under evaluation, their current state, their workers, and so on. In order to re- late this run-time information to the static blueprints, we let the modified Clean compiler also transform the task definitions and applications to pass compile time information to the iTasks run-time. This allows us to show how the blueprints are instantiated using the Tonic viewer, which is also written in iTasks.

In this chapter we focus on the technical challenges and make the following contributions:

• We define at what level of abstraction we generate blueprints, striking a balance between the level of detail and understandability by domain experts; • We define the blueprints by means of formally defined production rules; • We discuss implementation issues in implementing blueprint generation; • We show how we technically manage to instantiate blueprints at run-time

such that we can show which tasks in the blueprint are finished, are currently being executed, and what their actual parameters and current results are. The remainder of this chapter is structured as follows. In Section 3.2, we give a short overview of the iTasks DSL and present a running example. In Section 3.3, we show how and which static blueprints are generated. Section 3.4 explains how we manage to show the instantiation of blueprints at run-time. Section 3.5 reviews related work. Conclusions are presented in Section 3.6.

3.2

Short Overview of iTasks

The TOP paradigm, as embodied in iTasks, builds on a few core concepts: tasks, which define the work that needs to be done; editors, which are tasks that facilitate user interaction; combinators, to compose tasks from simpler ones; and shared data sources (SDSs), to handle shared information in a uniform way. Tasks are reactive and their current state can be observed. A task of type (Task a) processes a task value of type a, which may change over time while the work takes place. The value

is either absent: no value is available (yet), unstable: some value is available but it might change in the future or even become absent, or stable: the value is final. We illustrate these concepts by means of a small case study that models part of the operation of an emergency call center2:

1

:: Emergency = { time :: DateTime, info :: CallInfo }

2

:: CallInfo = { contact :: String, phone :: String, location :: Address

3

, situation :: String, authorities :: [Authority] }

4

:: Address = { city :: Maybe String, street :: Maybe String, no :: Maybe Int }

5

:: Authority = Ambulance | FireBrigade | Police

6

:: Verdict = Success | Fail String | FakeCall

7 8

derive class iTask Emergency, CallInfo, Address, Authority, Verdict

9 10

requiresAuthorities :: Emergency -> Bool

11

requiresAuthorities call = not (isEmpty call.info.authorities)

12 13

makeEmergency :: DateTime CallInfo -> Emergency

14

makeEmergency now data = {time=now, info=data}

15 16

emergencies :: Shared [Emergency]

17

emergencies = sharedStore "emergencies" []

18 19

main :: Task Verdict

20

main = handleEmergencyCall processPhoneCall

21 22

processPhoneCall :: Task Emergency

23

processPhoneCall = get currentDateTime >>= logCall

24 25

logCall :: DateTime -> Task Emergency

26

logCall now = makeEmergency now <$> enterInformation "Enter call information:" []

27 28

handleEmergencyCall :: (Task Emergency) -> Task Verdict

29

handleEmergencyCall intake = UserWithRole "call-intaker" @: (

30

intake >>* [ OnAction ActionContinue (ifValue requiresAuthorities coordinate)

31

, OnAction (Action "Fake call" []) (always (return FakeCall))])

32 33

coordinate :: Emergency -> Task Verdict

34

coordinate call

35

= upd (add call) emergencies

36

>>| UserWithRole "call-coordinator" @: (

37

allTasks (alertAuthoritiesAbout call) >>= showSuccessOfVerdicts)

38

where

39

add call calls = [call : calls]

40 41

alertAuthoritiesAbout :: Emergency -> [Task Verdict]

42

alertAuthoritiesAbout call=:{info={authorities}} = map (alert call) authorities

43

where

44

alert :: Emergency Authority -> Task Verdict

3.2. SHORT OVERVIEW OF ITASKS

45

alert {info={situation}} authority = enterInformation info []

46

where

47

info = join " " ["Alert", toSingleLineText authority, situation]

48 49

showSuccessOfVerdicts :: [Verdict] -> Task Verdict

50 showSuccessOfVerdicts verdicts 51 =case collectFailures of 52 [] -> return Success 53

fails -> let msg = join "\n" fails

54

in viewInformation "Uninformed authorities:" [] msg

55

>>| return (Fail msg)

56

where

57

collectFailures = [str\\ Fail str <- verdicts]

In iTasks, the entities of the problem domain are typically modeled with com- mon data types (record types, algebraic data types, synonym types, basic types). Pure functions are used to define relationships between these entities.

In the emergency call center example, emergency calls (of type Emergency) are received at some date and time (DateTime is a predefined type). During the intake of a call, an employee finds out and records the information (of type CallInfo) that is provided by the caller (lines 1-7). In addition, it must be determined which authorities need to be notified about the emergency. A call-intaker has been trained to determine if a call is fake. This results in a Verdict how the emergency call has been dealt with. The predicate requiresAuthorities checks whether an emergency calls for help by at least one authority. The function makeEmergency creates an Emergency value.

On line 8, instances of the iTask class for the indicated types are derived. This class consists of the predefined type driven generic functions that are used by the iTasks run-time system to handle GUI rendering, (de)serialization, persistent storage, and comparison of values, amongst others. Automatic deriving allows task engineers to concentrate on specifying the intended behaviour correctly when communicating with domain experts. The remainder of the specification defines the tasks that need to be performed when receiving an emergency call.

The main task handles an emergency call. To emphasize the fact that the way in which the call information is received is less relevant, the main task is implemented as the higher-order task function handleEmergencyCall, which is parameterized with a task function that abstracts from the exact way in which the call is received. In this case, processPhoneCall models how phone calls are processed. In processPhoneCall, currentDateTime is an SDS that holds the current date and time. The task function get obtains the current value of the SDS. This value is passed to the logCall task using the monadic >>= combinator. The logCall task is interactive: enterInformation is an editor that creates a user interface with which, in this case, only values of type CallInfo can be created (see Figure 3.1 (left)). Editors never reach a stable task value because the user can, at any time, decide to continue working on the current task value. We map makeEmergencyover the editor to turn the CallInfo task value into an Emergency structure using the earlier retrieved date and time value. As a result, the context

Figure 3.1: The rendering of the handleEmergencyCall task function (left). The rendering using viewInformation of the same information after adding date and time (right)

in which the processPhoneCall task is executed, handleEmergencyCall, observes the Emergency value, if any, to decide how to proceed.

The handleEmergencyCall task assigns (@:) the task of handling an emer- gency call to a call-intaker, who executes the intake task prescription and decides whether the call must be acted upon or is a fake.

The core combinator that observes a task and specifies the follow-up tasks is the step combinator >>*. Follow-up tasks can either be handled fully autonomously by iTasks, or require triggering by the current worker before they are handled autonomously. The autonomous part is a computation that, depending on the observed task value, potentially returns a follow-up task Maybe (Task b). The example uses two predefined patterns: ifValue c t tests only (un)stable task values with c and if the condition holds, proceeds as t; always t always returns t regardless of the (availability of the) task value. Worker-triggers are specified as (OnAction a f ) values that provide the worker with a user-interface based on a (usually a button), that initiate evaluation of the autonomous computation f to compute the follow-up task. In Figure 3.1 (left), the two actions are rendered as buttons at the lower-right bottom of the screen. Follow-up task specifications that consist only of an autonomous part f are specified as (OnValue f ). It should be noted that the monadic >>= combinator is implemented in terms of >>*. It proceeds to its right-hand side task in case the left-hand side task has a stable value, or when the user clicks its “Continue” button.

The first job of the coordinate task is to log the call in the emergencies SDS. Next, a worker with role call-coordinator contacts all requested authorities. This is an example of a parallel task composition. The core combinator in these situations is parallel. The expressive power of this general purpose combinator is not always used (it can handle a dynamic number of potentially distributed tasks), so frequently occurring parallel work patterns are o↵ered by iTasks (e.g.

3.3. STATIC TONIC BLUEPRINTS

Documento similar