Transcript

Task Oriented Programmingin

using

Rinus Plasmeijer – Bas Lijnse - Peter Achten Pieter Koopman - Steffen Michels - Jurriën Stutterheim

Jan Martin Jansen (NLDA) - Laszlo Domoszlai (ELTE)ASSIGNMENTS - ANSWERS

1

1. Getting started: additional information

Once you have successfully created an iTask application and started it, you and anybody can connect to it via a web browser.

If you only want to test the application for yourself, it is sufficient to navigate to http://localhost/.

If you want other people to use your application as well (as with the Ligretto case study), then these persons need to navigate to the IP-address of your computer.

In both cases, the application uses the web browser for the interaction with your program(s).

2

2. Tasks panel

• The function basicAPIExamples in the main module BasicAPIExamples.icl enumerates all tasks that can be explored by the user(s).

• An element in this list is specified with:

workflow task_tree_spec brief_explanation task

in which:

•task_tree_spec:is a string of shape g1/g2/…/gn/task_name such that the names gi are used to group similar tasks, and task_name is the name of the task

•brief_explanation:is a short string that explains the purpose of executing this task

•task:a task of any arbitrary result value

3

3. Persistent tasks

• In the iTask system all tasks are persistent, i.e.: their state is stored in background storage at all times.

• Consequently, it does not matter if you stop working on that task for a period of time, or simply close your browser and reconnect later.

• It is possible to completely erase all background storage. On your computer, navigate to the folder in which your main module is located. Suppose your main module is called 'main.icl'. You should find a folder called 'main.exe-data'. Simply remove this folder.

• For instance, in the case of BasicAPIExamples, navigate to '\iTasks-SDK\Examples\' and remove the folder 'BasicAPIExamples.exe-data'

4

4. Custom types

5

• The iTask system is a domain specific language for defining dynamic workflow style application. It is embedded in the host language Clean.

• The implementation of the iTask system uses the generic programming facilities of Clean to automatically create web pages for any custom data type that you can think of.

• If you create a new type and want the iTask system to be able to use it, then you need to invoke the generic programming mechanism for that type.

• In the case of the MyPerson type, this looks as follows:derive class iTask MyPerson

• If you forget to include this derive class declaration in your code you will get swamped with lots of compiler error messages, e.g.:

Overloading error [BasicAPIExamples.icl,66,Start]: "JSONEncode_s" no instance available of type MyPersonOverloading error [BasicAPIExamples.icl,66,Start]: "gVisualizeText_s" no instance available of type MyPersonOverloading error [BasicAPIExamples.icl,66,Start]: "gHeaders_s" no instance available of type MyPersonOverloading error [BasicAPIExamples.icl,66,Start]: "gGridRows_s" no instance available of type MyPersonOverloading error [BasicAPIExamples.icl,66,Start]: "gDefault_s" no instance available of type MyPerson…

5. Customized view

6

• The basic interactive tasks enterInformation, update(Shared)Information, view(Shared)Information, and so on can be fine-tuned by means of a list of options.

• Currently, these options concern only the way the task value is rendered.

• In a future version, more option-types will be added to these lists.

Intermezzo

• During the lab sessions we suggested to put new Clean implementation and definition modules in the same folder as the BasicAPIExamples.icl module.

• It is better practice to store modules that belong to each other in a separate module.

• In that case you need to add the directory-path to these modules in your project file. In the CleanIDE this can be done as follows:

• choose 'Project:Project Options…'

• this opens the project window (right)

• choose 'Project Paths'

• this opens the project paths window

• choose the 'Append…' button

• navigate to the folder(s) that you wish toadd

• choose the 'OK' button to confirm

7

6. Play with HTML

• The Html type is quite a large algebraic data type. You can find its definition in module HTML.dcl. If you are already familiar with html, then you probably recognize most of the tags being coded as data constructors of this type.

• The Html and HtmlAtts types are examples of deeply embedded domain specific languages, in this case for representing html.

8

7. Viewing a Player

• A possible solution to view_player is the following:view_player :: Player -> Task Playerview_player player = viewInformation ("Player " <+++ player.color) [ViewWith player_view] player

player_view :: !Player -> HtmlTagplayer_view player = TableTag [BorderAttr "2"] [view_row player.row

,view_ligretto player.ligretto ,view_hand player.hand ]

• This definition shows, in left-to-right order, the row, ligretto pile, and the two hand piles (concealed and discarded cards) of the player.

9

8. Viewing the middle piles

• A possible solution to view_piles is:view_piles :: [Pile] -> Task [Pile]view_piles piles = viewInformation "Piles" [ViewWith piles_view] piles

piles_view :: ![Pile] -> HtmlTagpiles_view piles = TableTag [BorderAttr "2"]

[ TrTag [] [ if (isEmpty pile) (TdTag [] [Text "empty!"]) (view_card Front (hd pile)) \\ pile <- piles ] , TrTag [] [ TdTag [] [Text (toString i)] \\ i <- [1..length

piles] ] ]

• this definition shows all piles of cards in a single row; underneath each (empty) pile a number is shown as well (1 upto maximum number of piles)

10

9. Add people

• The iTask system is a distributed, multi-user system. The currently registered users are stored in a shared data source, called userAccounts (module UserAdmin.dcl).

• This module contains a number of useful functions to get access to the current users (shared data source users), find users with a certain role (usersWithRole), authentication (authenticateUser), and more…

11

10. Invite friends

• A solution to invite_friends is:invite_friends :: Task [User]invite_friends = enterSharedMultipleChoice "Select friends to play with" [] users >>= \friends -> if (not (isMember (length friends) [1..3]))

( viewInformation "Oops" [] "number of friends must be 1, 2, or 3" >>| invite_friends )

( return friends )• This defines a recursive task that keeps asking the current user to

enter 1 upto 3 persons from the users shared data source

12

11. Shared data source

• Solutions to middle_state and player_state shared data sources are:middle_state :: Shared Middlemiddle_state = sharedStore "middle" (repeat 16 [])

player_state :: Color -> Shared Playerplayer_state color = sharedStore ("player " <+++ color) {color = color, row = [], ligretto = [], hand = {conceal = [], discard = []}}

• The first argument of the sharedStore function is used as unique identifier of the shared data store that is to be created. Therefor, the players are identified using their color.

• The second argument of the sharedStore function is the initial value of the shared data store.

• In case of the Ligretto case, the initial player value is overwritten once a game starts, providing the player with cards.

13

12. Viewing the middle piles, now as shared view

• Solutions to viewing the piles and players are (changes are underlined):view_player :: Player -> Task Playerview_player player = viewSharedInformation ("Player " <+++ player.color) [ViewWith player_view] (player_state player.color)

view_piles :: Task [Pile]view_piles = viewSharedInformation "Middle" [ViewWith piles_view] middle_state

• Instead of looking at a particular value (with viewInformation) your application now looks at a shared data source (with viewSharedInformation). As an effect, whenever the shared data source is altered the view is updated is well.

• Note that the altered view_piles task no longer requires a [Pile] argument, as it gets its information from the middle_state shared data source.

14

13. One player plays a game of Ligretto

• A possible solution to the game function is:game :: NrOfPlayers Color -> Task Colorgame nr_of_players color

= get randomInt >>= \r -> let player = initial_player nr_of_players color (abs r) in set player (player_state color) >>| view_piles ||- view_player player ||- play_cards nr_of_players player

obtain a random number from the randomInt shared data source create a new player with shuffled cards and proper row, ligretto, and hand set this new player value in the corresponding shared data source look at the middle piles look at yourself start to play cards according to the rules yet to be defined in play_cards

15

14. Playing the cards

• The suggested structure for the play_cards task is:play_cards :: NrOfPlayers Player -> Task Colorplay_cards nr_of_players player= watch (player_state color >+< middle_state)>>*

• (watch sds) is a task that continuously keeps an eye on the read value of sds. The task value of (watch sds) is always that of the read value of sds.

• (sds1 >+< sds2) turns two separate shared data sources, sds1 and sds2, into one shared data source. The read value of the new shared data source is (r1, r2), with ri the read value of sdsi. The write value of the new shared data source is (w1, w2), with wi the write value of sdsi.

• (t >>* steps) is a new task that performs task t while keeping an eye on the task value tv of t. The list steps enumerate all possible ways to continue from here, based on tv. There are four kinds of steps:

– (OnValue tf): perform a task depending on (tf tv)– (OnAction action tf): provide user with action to do a task depending on (tf tv)– (OnException tf): catch specific exception e and do task depending on (tf e)– (OnAllExceptions tf): catch any exception as str and do task depending on (tf

str)

16

14. Playing the cards

• The suggested structure for the play_cards task is:play_cards :: NrOfPlayers Player -> Task Colorplay_cards nr_of_players player

= watch (player_state color >+< middle_state)>>*

• Finish with appropriate actions (OnAction):1. the player can put a row card on a (new) pile in the middle2. the player can put the top discard hand card on a (new) pile in the

middle3. the player can move the top three cards from conceal to discard pile4. the player can shuffle all discard cards to restart with the conceal pile

• Finish with an appropriate predicate (OnValue):– immediately when the ligretto pile is empty, return the color of the

player

17

14. Playing the cards:1. put a row card on a (new) pile in the middle

• Offer the actions to the user (summarize with a list comprehension):[ OnAction (Action ("Play card " <+++ cardnr) []) (play_card nr_of_players player cardnr) \\ cardnr <- [1 .. nr_of_cards_in_row nr_of_players]]

• The action can only be performed if a middle pile matches:play_card :: NrOfPlayers Player Int (TaskValue (Player,Middle)) -> Maybe (Task Color)play_card nr_of_players player cardnr (Value (me, middle) _)| not (isEmpty matching_piles)

= let (index,pile) = hd matching_piles in Just ( update (updateAt index [card : pile]) middle_state >>| set (move_ligretto_card_to_row cardnr me) (player_state

player.color) >>| play_cards nr_of_players player )

where card = row_card cardnr me matching_piles = [ (index,pile) \\ pile <- middle & index <- [0 ..]

| card_matches_top_of_pile card pile ]play_card _ _ _ _ = Nothing

18

14. Playing the cards:2. put top discard card on a (new) pile in the

middle• Offer the action to the user:

OnAction (Action "Play hand" []) (play_hand nr_of_players player)

• The action can only be performed if the discard pile has a card and a middle pile matches:

play_hand :: NrOfPlayers Player (TaskValue (Player,Middle)) -> Maybe (Task Color)play_hand nr_of_players player (Value (me, middle) _)| isJust maybe_card && not (isEmpty matching_piles)

= let (index,pile) = hd matching_piles in Just ( update (updateAt index [card : pile]) middle_state >>| set (remove_top_of_discard me) (player_state player.color) >>| play_cards nr_of_players player )

where maybe_card = top_discard me card = fromJust maybe_card matching_piles = [ (index,pile) \\ pile <- middle & index <- [0..]

| card_matches_top_of_pile card pile]play_hand _ _ _ = Nothing

19

14. Playing the cards:3. move top 3 concealed cards to discard pile

• Offer the action to the user:OnAction (Action "Next hand" []) (next_hand nr_of_players player)

• The action can only be performed if the concealed pile is not empty:next_hand :: NrOfPlayers Player (TaskValue (Player,Middle)) -> Maybe (Task Color)next_hand nr_of_players player (Value (me, middle) _)| not (isEmpty conceal)

= Just ( set (swap_discards me) (player_state player.color) >>| play_cards nr_of_players player )

where conceal = me.hand.concealnext_hand _ _ _ = Nothing

20

14. Playing the cards:4. shuffle all discard cards and make them

concealed• Offer the action to the user:

OnAction (Action "Shuffle hand" []) (shuffle nr_of_players player)

• The action can only be performed if the concealed pile is empty:shuffle :: NrOfPlayers Player (TaskValue (Player,Middle)) -> Maybe (Task Color)shuffle nr_of_players player (Value (me, middle) _)| isEmpty conceal

= Just ( get randomInt >>= \r -> set (shuffle_hand (abs r) me) (player_state player.color) >>| play_cards nr_of_players player )

where conceal = me.hand.concealshuffle _ _ _ = Nothing

21

14. Playing the cards:5. return when ligretto pile is empty

• Offer the predicate to the step combinator:OnValue player_wins

• Return color of player only if the ligretto pile is empty:player_wins :: (TaskValue (Player,Middle)) -> Maybe (Task Color)player_wins (Value (me, middle) _)| isEmpty me.ligretto = Just (return me.color)player_wins _ = Nothing

22

14. Playing the cards: include 1. upto 5.

• Thus, the complete solution for the play_cards task is:play_cards :: NrOfPlayers Player -> Task Colorplay_cards nr_of_players player = watch (player_state color >+< middle_state) >>* ( [ OnAction (Action ("Play card "<+++cardnr) []) (play_card nr_of_players player cardnr)

\\ cardnr <- [1 .. nr_of_cards_in_row nr_of_players] ] ++ [ OnAction (Action "Play hand" []) (play_hand nr_of_players

player) , OnAction (Action "Next hand" []) (next_hand nr_of_players

player) , OnAction (Action "Shuffle hand" []) (shuffle nr_of_players

player) , OnValue player_wins

] )

23

15. All players play a game of Ligretto

• A possible solution to the main task play_ligretto is:play_ligretto :: Task Colorplay_ligretto = get currentUser

>>= \me -> invite_friends >>= \friends -> set (repeatn 16 []) middle_state>>| let nr_of_players = length friends + 1 in anyTask [ player @: game nr_of_players color \\ player <- [me : friends] & color <- colors nr_of_players ]>>= \winner -> allTasks [ player @: (viewInformation "The winner is:" [] winner

>>= return) \\ player <- [me : friends] ]>>| return winner

24


Top Related