Part 1: Working with async ========================== Async in utop ------------- **Note**: don't forget to create a .ocamlinit and a .cs3110 file as explained in the last lab. Utop has rudimentary support for evaluating deferred expressions. When you enter an expression of type 'a Deferred.t into utop, utop will run the async scheduler until the deferred becomes determined, and print out the contents of the deferred instead of the deferred itself. Unfortunately, there is a bug in the version of utop that was distributed with the 3110 VM that prevents this feature from working properly. You can install a patched version of utop by executing the following commands in the terminal:  $opam pin add utop https://github.com/cs3110/utop.git$ opam update utop \$ opam upgrade utop  This will download, compile, and install the patched version of utop. **Exercise**: after running the above commands, run utop. Type the expression after (Core.Std.sec 2.);;. Observe the output. Evaluate the expression after (Core.Std.sec 2.) >>= fun () -> return 3;; and observe the output. Utop does **not** continuously run the scheduler in the background. It only runs the scheduler when you type in an expression with type Deferred.t, and it stops running the scheduler when that deferred becomes determined. **Exercise**: In utop, evaluate the expression  upon (after (Core.Std.sec 1.)) (fun () -> Printf.printf "hello\n");;  You will not see any output when the after deferred becomes determined, because the scheduler is not running. If you later cause the scheduler to run (by evaluating a deferred), you will see the output. Evaluate after (Core.Std.sec 0.);;. **Exercise**: typing Core.Std.sec and after (Core.Std.sec 0.);; can be tiresome. Add the following to your .ocamlinit file (in addition to the lines suggested in the previous recitation):  let sec = Core.Std.sec;; let flush () = after (sec 0.);;  Restart utop. Evaluate  utop# upon (after (sec 1.)) (fun () -> Printf.printf "hello\n");; utop# flush ();;  Testing with async ------------------ Writing unit tests for async code is difficult, because our unit testing framework only works with non-deferred values, while async code usually produces deferred values. There is a function that can be used to allow non-async code (like the testing framework) to interact with async code. It is called Thread_safe.block_on_async. Thread_safe.block_on_async has type (unit -> 'a Deferred) -> ('a,exn) Core.Std.Result.t. Thread_safe.block_on_async f runs the async scheduler until the deferred that f returns becomes determined with value v. It then evaluates to Core.Std.Result.Ok v. If f raises an exception e, then block_on_async evaluates to Core.Std.Result.Error e. **You must NEVER use block_on_async except in your TESTs**. If you are tempted to do so, you should use (>>=) instead. You can use block_on_async to implement unit tests. For example, to test that the deferred expression f 1 returns 2, you can first use block_on_async to block until f 1 becomes determined. Then you can compare the output to Core.Std.Result.Ok 2:  TEST = Thread_safe.block_on_async (fun () -> f 1) = Core.Std.Result.Ok 2  Of course, this is rather ugly, so you may want to create a helper function to do these checks for you:  let test_async_eq (d : 'a Deferred.t) (v : 'a) : bool = Thread_safe.block_on_async (fun () -> d) = Core.Std.Result.Ok v  Then the above test just becomes  TEST = test_async_eq (f 1) 2  **Exercise**: The file [testing.ml](rec_code/testing.ml) contains a simple function for deferred addition. Complete the "check add" unit test. Compile it, and then run it with cs3110 test. One way a deferred computation can fail is by never determining its output. In The function with_timeout takes a time span t and a deferred d, and returns a deferred that becomes determined with value  Result v if d becomes determined with value v before t seconds have elapsed. Otherwise, it becomes determined with value  Timeout. **Exercise**: Use the test_async_eq function from above to test the function called broken in testing.ml. Compile and run the test, note the behavior (as always, you can stop the program with control+C if needed). Create a new function test_async_eq_with_timeout : 'a Deferred -> 'a -> bool, and use it to test both broken and add. add should pass the test, but broken should not. Part 2: Functional observer pattern with Ivars ============================================== A common pattern that arises in asynchronous programs is that different modules are responsible for interacting with different parts of the outside world. For example, when developing an networked graphical adventure game, you might have a Player module responsible for keeping track of a player's position, a Network module responsible for communicating over the network, and a Gui module responsible for keeping the GUI up to date. When the state contained in the Player module changes (for example if the user decides to move to a different room), both the Gui and the Network need to respond (the Network module might broadcast the player's new position, while the Gui module might change where the player is drawn on the screen). One way to accomplish this would be to have the Player module notify the other modules by calling update functions on them. This design can lead to complicated code, because the Player module needs to know about all of the modules that are using it. If you decided to add a Logging module in the future that keeps a log of all of the game changes, you would have to modify the code in the player module to call functions in the Logging module. The Player module would be tightly coupled with many parts of the system. An alternative approach is a functional version of the "observer pattern". The Player module can expose a deferred value that becomes determined when the state of the player changes. The modules that are interested in responding to changes in the player state can bind a callback to this deferred that responds appropriately to the changes in the player state. The observer pattern is not specific to async. It is commonly employed in object oriented programs. It is also related to other functional programming paradigms called "flow based programming" and "functional reactive programming". ### Guidelines for implementing the subject module The module that implements the changing state is called the "subject module". In the adventure game example, Player would be the subject module. The subject module will typically contain a "state type" representing the changing state. For example, the Player module might define a Player.t type to represent a single player.  (* in player.mli *) type t val position : t -> Vector.t val inventory : t -> Item.t list val mood : t -> Mood.t val updated : t -> t Deferred.t  - The state type will typically be a record containing the important variables (such as position, inventory and mood in the adventure game example). It will also contain an updated Ivar that will be filled when the state changes:  (* inside player.ml *) type t = { position : Vector.t; mood : Mood.t; inventory : Item.t list; updated : t Ivar.t; }  - Although we think of the state as changing over time, it is simpler to implement the state as an immutable data structure; to "update" a Player.t, we create a new record, and fill the updated Ivar of the old record with the new version:  (* inside player.ml *) let handle_keypress () = ... Ivar.fill player.update { position : player.position; mood : new_mood; inventory : player.inventory; updated : Ivar.create (); }  - It can be cumbersome to write this code every time you want to update the player; it is likely that most of the functions in the Player module will update player values often. It is useful to factor the update into a separate function that takes a state with new fields but the old updated Ivar, and takes care of filling the Ivar and creating the new updated Ivar:  (* inside player.ml *) let do_update player = let new_player = {player with updated = Ivar.create()}; Ivar.fill player.updated new_player; new_player  With do_update defined, you can conveniently update the fields using functional record update:  let handle_keypress () = ... do_update {player with mood = Grumpy}  **Exercise**: The file [student.mli](rec_code/student.mli) contains the interface for a student type (Student.t). Students can either be sleeping or working (these are the only options). The sleep function changes a student from working to sleeping, while the work function wakes the student up. - Implement the Student module in student.ml, using the guidelines given above. - In your working file for the recitation, create a student s. Use upon to schedule a message to be printed if the student goes to sleep. - In utop, #use your working file. Call Student.sleep on your student to cause the student to sleep, and observe the output (don't forget to call flush to see the output). Call Student.work and then call Student.sleep again. Notice that there is no output. ### Implementing observers There is no output on the second call to sleep because you have only called upon on the first version of the student, so your function only gets called the first time the student is updated. If you want to implement an observer function that gets called for _every_ change in the subject, you need to schedule it recursively. For example:  let rec observe_player player = upon (updated player) (fun player' -> observe_player player'; printf "player's new mood is %s\n" (Mood.to_string player'.mood) )  This way, whenever the player is updated, the new version of the player is also observed for future updates. **Exercise**: modify your working file for the recitation so that a message is printed _every_ time s falls asleep. In utop, call sleep and work on s, and observe the output. If you don't see any output, don't forget to flush! **Exercise (\*) **: The file [studentSet.mli](rec_code/studentSet.mli) contains an interface for a set of students. A student set keeps track of a collection of students. 1. Implement the StudentSet module - Create a skeleton implementation of studentSet.ml. Implement all of the functions with failwith "TODO". Define the StudentSet.t type as a record containing a list of sleeping students, a list of awake students, and an Ivar for tracking changes. - Implement create, all_students, sleeping_students, and working_students - Implement add_student. Be sure to schedule a function to update the StudentSet as necessary when the student changes. 2. Use the StudentSet module - in your working file for the recitation, create a StudentSet. Create two students and add them to the StudentSet. - As in the previous exercise, schedule a function to print a message whenever one of your students falls asleep. - Schedule a function to print a message whenever all of the students in the student set fall asleep. The message should only be printed when there were students who were awake. For example, if there were no working students and a new sleeping student is added, then the message should not be printed. - In utop, experiment with putting your students to sleep and waking them up.