Home » Teaching » CSCI 49380/79526: Reactive Programming (FA ’20)

Subscribe

Archives

Categories

Attribution-NonCommercial-ShareAlike 4.0 International

Except where otherwise noted, content on this site is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license.

CSCI 49380/79526: Reactive Programming (FA ’20)

Lectures

  1. Java Overview
  2. Scala Basics
  3. Control Structures and Functions
  4. Arrays, Maps, Tuples
  5. OOP1: Classes and Objects
  6. OOP2: Packages, Inheritance, and Traits
  7. Functional Programming
  8. Case Classes and Pattern Matching
  9. Collections
  10. Futures
  11. The Actor Programming Model
  12. Distributed Computing with Actors (Eventual Consistency)
  13. Handling Failures and Actor State
  14. Typed Actors
  15. Akka Stream Processing
  16. Automated Refactoring to Reactive Programming
  17. Eliminating Abstraction Overhead of Java Stream Pipelines using Ahead-of-Time Program Optimization
  18. Safe Stream-Based Programming with Refinement Types

Assignments

Assignment 1

  1. What are the downsides of the uniform access principle? Specifically, is it always beneficial for clients to not know whether they are accessing a field or calling a method? Think of a concrete example of where this may be problematic.
  2. Complete the second part of the lab from lesson 4. For question f, you may use C++ or C# if you are not familiar with Java.
  3. Consider the following class definition:
    class Account {
        val id = Account.newUniqueNumber()
        private var balance = 0.0
        def deposit(amount: Double) {balance += amount}
    }
    
    1. Add an accessor for balance without changing its current field declaration with the exception of its name.
    2. Override the toString method using string interpolation.
    3. Change deposit() to return the new balance.
  4. Also consider the following companion object for Account:
    object Account {
        private var lastNumber = 0
        private def newUniqueNumber() = {lastNumber += 1; lastNumber}
    }
    
    When is this object instantiated?

Assignment 2

  1. During lecture, we simulated a FileLogger trait using println(), which, obviously, does not input into a file. Implement a real Scala trait—based on our lecture—that logs to a file.

  2. During lecture, we discussed an object-level mix-in that logs both to a file and to the console:

    val acct3 = new SavingsAccount with ConsoleLogger with FileLogger
    
    1. Where does the log go to first, the file or the console? Why?
    2. How would you reverse the order?
    3. Add a time stamp to both logs using a TimestampLogger trait.
    4. Does it matter where you put TimestampLogger in the list of traits?
    5. What happens if you place TimestampLogger between ConsolLogger and FileLogger?
  3. Consider the following Scala code:

    var acct: SavingsAccount = new SavingsAccount
    val acct2: SavingsAccount = new SavingsAccount with FileLogger
    
    1. What is the relationship between the run-time types of the objects referred to by acct and acct2? Explain this relationship.
    2. Can acct2 refer to the object referred by acct? Why or why not?
    3. Can acct refer to the object referred by acct2? Why or why not?
  4. Explain the difference between the eq and equals methods.

    1. We discussed that application programmers should always use == instead of eq or equals. Can you think of a scenario where this is not true? Explain the use case.

Assignment 3

Notes

Arguments vs. Parameters

The below questions refer to the terms "arguments" and "parameters." As a review, the term argument refers to the value passed to a function or method, while the term parameter refers to the names listed in a function’s definition. Correspondingly, if you are asked to alter an argument, you are to alter the function invocation. Conversely, if you are asked to alter a parameter, you are to alter the function definition.

Questions

  1. Create a higher-order function called composite that takes two (Double) => Double functions and a double and applies the first function to the return value of the second function given the double argument.

  2. Import the Scala Math library. Then, apply the composite function to the arguments ceil, floor, and 5.5. Show the correct code on how to invoke composite using these arguments. What does composite return given these arguments?

  3. Rearrange the arguments so that composite returns 6.0. Show the code invoking composite to obtain this value.

  4. Create a higher-order function called composite that takes two (Double) => Double functions and returns a function that applies the first function parameter to the second function parameter.

  5. What is the type of this function?

  6. Store this function in a constant called compFunc. Show the code for how to do this.

  7. Compose ceil and floor using compFunc and store it in a constant called upAndDown. Show the code.

  8. Call upAndDown, giving 5.5. What value did you get? Explain how this works.

  9. Show the code to compose floor and ceil directly, i.e., without higher-order functions, again giving 5.5. Do you get the same answer?

  10. During lecture, we saw how to generate a sequence $0.1,0.2,ldots,0.9$ using (1 to 9).map(0.1 * _). Write the code to make (0.1 to 0.9) work correctly by avoiding the map function.

Assignment 4

  1. Consider the following Scala code:

    var sign = // ...
    val ch: Char = // ...
    var digit: Integer = _
    ch match {
    	case _ => digit = Character.digit(ch, 10)
    	case '+' => sign = 1
    	case '-' => sign = -1
    	case _ => sign = 0
    }
    

    This code has a warning. Explain what the warning is and why it is given.

  2. The following code fixes the warning by altering the first case’s code:

    ch match {
    	case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10)
    	case '+' => sign = 1
    	case '-' => sign = -1
    	case _ => sign = 0
    }
    

    Why does this change fix the warning?

  3. Consider the above case expression with a guard case _ if Character.isDigit(ch) => digit = Character.digit(ch, 10). In effect, this case expression says "match everything but also check the condition Character.isDigit(ch)." One way to think of this is that there are actually two guards here, the first matches ch to _, which always matches, and the second checks whether Character.isDigit(ch) returns true. What if we were to replace _ with another character, say, ?, which now will only match a single character? Since we have two boolean conditions, they must be combined in some way. Are they combined using logical conjunction or disjunction? Explain.

  4. Consider the statement val (x, y) = (1, 2). Does this statement declare and assign two constants x and y to 1 and 2, respectively, or does it declare and assign a tuple constant (x, y) whose value is (1, 2)? Explain.

  5. Consider the following valid Scala code: val 2 = x. This code throws a scala.MatchError when executed. According to the Scala Standard Library 2.13.3, "MatchError implements errors [that] are thrown whenever an object doesn’t match any pattern of a pattern matching expression." What part of this statement constitutes the pattern?

  6. Consider the Bundle case class:

    sealed abstract class Item
    case class Article(description: String, price: Double) extends Item
    case class Bundle(description: String, discount: Double, items: Item*) extends Item
    

    In the primary ctor for Bundle, parameter discount refers to an absolute discount (e.g., $20 off) as opposed to a relative discount (e.g., 20% off). It would be helpful to have another Bundle case class that represents bundles related to relative discounts. To do so, one may consider subclassing Bundle with another case class, e.g., BundleWithRelativeDiscount, to leverage common functionality; however, such an activity is ill-advised. Specificaly, if multiple levels of inheritance are required to factor out common functionality of case classes, only the leaves of the inheritance hierarchy should be case classes.

    To achieve this, remove case from the Bundle declaration above and, instead, make it abstract (other changes may also be required). Then, create two new case classes, BundleWithAbsoluteDiscount and BundleWithRelativeDiscount to accomodate the above stated situation. Finally, alter the price method (below) to (i) remove the old Bundle case class and (ii) accomidate the two new case classes:

    def price(it: Item): Double = it match {
    	case Article(_, p) => p
    	case Bundle(_, disc, its @ _*) => its.map(price _).sum - disc
    }
    

    Test your new code using the following constant:

    val special = BundleWithRelativeDiscount("Father's day special", 20.0,
    	Article("Scala for the Impatient", 39.95),
    		BundleWithAbsoluteDiscount("Anchor Distillery Sampler", 10.0,
    			Article("Old Potrero Straight Rye Whiskey", 79.95),
    			Article("Junípero Gin", 32.95)))
    

    What is the value you receive from invoking price(special)?

Assignment 5

  1. We saw in lecture that we can check if one set is a subset of another:

    val digits = Set(1, 7, 2, 9)
    Set(1, 2) subsetOf digits // true
    

    Is Set(2, 1) also a subset of Set(1, 7, 2, 9)? Explain.

  2. Is List(2, 1) a subset of List(1, 7, 2, 9)? In other words, what does List(2, 1) subsetOf List(1, 7, 2, 9) return? Explain. If the code does not compile, explain why not.

  3. Why is subsetOf not provided for Lists?`

  4. Consider the following Scala code:

    val digits = Set(1, 7, 2, 9)
    val primes = Set(2, 3, 5, 7)
    val diff = digits -- primes 
    
    1. What is the value of diff?
    2. Does digits - primes give us the same result?
    3. If the above results in a compilation error, explain why.
    4. Why does Scala not overload the - operator such that it removes elements when a single element is given but results in set difference when another set is given?
  5. Consider the following Scala code:

    def numsFrom(n: BigInt): LazyList[BigInt] = n #:: numsFrom(n + 1)
    val squares = numsFrom(1).map(x => x * x)
    

    What would happen if you called squares.force? Explain.

  6. During lecture, we mentioned that the apply method, i.e., using (i) on a view where i is an element index, of lazy views evaluates the entire collection.

    1. Why is this potentially dangerous?
    2. How can we avoid this problem?

Assignment 6

Answer question #3 from the Collections lab.

Presentations

  • Presentations will start on 11/24 and go to 12/15, the day of our “final exam.”
  • Each talk will be 25 minutes long. An additional 5 minutes will be allotted for questions.
  • We will have four talks each week.
  • Topics will be presented in the order listed. Papers will be presented after the topics.
  • For topics, I can give you resources, which include the optional book (see syllabus).
  • Students may lobby the instructor if there is an unlisted paper related to the student’s interest in reactive programming.

Presentation Groups

Please sign up for one presentation group of two members. You will work with your partner on the presentation and share the responsibilities. In other words, both members must present. Groups must be solidified by the beginning of class on 11/17.

Possible Presentation Topics and Papers

Topics

  1. The actor programming model.
  2. Handling failures and actor state.
  3. Distributed computing with actors: Eventual consistency.
  4. Typed actors.
  5. Akka stream processing.
  6. Stream failure handling and processing rate.
  7. Advanced stream processing.

Papers

  1. Automated Refactoring to Reactive Programming
  2. DiffStream: Differential Output Testing for StreamProcessing Programs
  3. Eliminating Abstraction Overhead of Java Stream Pipelines using Ahead-of-Time Program Optimization
  4. Actor Concurrency Bugs: A Comprehensive Study on Symptoms, Root Causes, API Usages, and Differences
  5. Tackling the Awkward Squad for Reactive Programming: The Actor-Reactor Model
  6. Safe Stream-Based Programming with Refinement Types
  7. BigFuzz: Efficient Fuzz Testing for Data Analytics Using Framework Abstraction
  8. An Empirical Study of Method Chaining in Java
  9. Java Stream Fusion: Adapting FP mechanisms for an OO setting

Project

Project Ideas

Feel free to edit the list of project ideas, but I must approve of new ideas once you decide to pursue them. However, you can list new ideas without my approval (approval is only required to pursue the new idea as your project).

  • Annotated bibliography  (10-15 papers, 1-page summary each, good for finding future research ideas).
  • Make a small HTTP server using Akka Actors.
  • Process a real-time stream of tweets from Twitter using Akka Streams and find some insights.
  • Make a small GUI game in Scala using Futures.
  • A customized project related to your ongoing research (requires instructor approval and consultation).
  • Build a “responsive” non-trivial web app using Scala and the Play Framework (you must use Futures and explain how they make your web app more responsive).
  • Build several functional, reactive microservices using Akka, Play, and Lagom. Call the services from some sort of client.

Project Groups

You must sign up for a group even if you are working alone. Otherwise, you can work in groups of two. There are 28 groups in case everyone wants to work alone, but I would imagine that is highly unlikely. You can work with the same partner from the presentation group if you wish, or you can work with a different partner. Up to you.

Project groups must be solidified by EOD 11/24. You must choose a project to work on. Multiple groups can work on the same project but cannot work together; you can only work with your group on the chosen project. You can choose one of the projects from the project ideas or your own project, pending my approval. See the syllabus for more details.

Groups will present their projects to me personally (aka via video conference) during finals week. You will need to make an appointment with me to do so.

Resources