1 Monad

Honestly, I don’t understand it. But I collect my thoughts about it here. First, the monda is used to chain operations and if the first (preceding) operation fails, the whole chain stops. An example that I understand is the usage of for-expression with pattern match. If the pattern matches, go next operation, else skip this element.

Another point of view is about the unit operation. A monad flatMap with its unit operation will return the monad itself.

My conclusion:

A monad is a container that supports operation chain and follows the monad laws. The basic operations of this container are flatMap and unit, where flatMap actually accepts a function that maps each element to a container, then binds these containers together; unit returns a container given an element. Monad laws guarantee the reliability of operation chain on these contains.

Monad seems like a design pattern that enables the operation chain along with pattern match in functional programming. Because flatMap chain with different functions is equivalent to nested flatMap, the for-expression is a syntax sugar based on flatMap chain. The advantage of this design pattern is avoding side effect.

Generator is created by yield as well as for-expression. Pay attention, there is no generator class/type/trait. The genrator created through for-expression has the same type of the sequence/iterator/stream used in for-expression. That proves the for-expression is a syntax sugar of monad types. So, guess what will happen when we create a for-expression using both list and stream?

val fibo:Stream[Int] = 1 #:: 1 #:: fibo.zip(fibo.tail).map(x=>x._1+x._2)
val l = List(1,2,3,4,5)
val lsg = for{x <- l; fib <- fibo} yield (x,fib)// infinite loop, crash the software/your machine
val slg = for{fib <- fibo; x <- l} yield (x,fib)// return a stream

Following the definition of Monad, the first mixture lsg actually makes l flatMap (x => fibo), which returns a List that tries to expand infinite stream fibo, hence block your machine. The second mixture slg returns a Stream that expand the list l, hence, works well. Besides, I have to clarify one thing: different monads demonstrated above all accept GenTraversableOnce as parameter and return monad of their own type. That is why they can be mixed together and the first expression decides the type of the final output of for-expression.

2 ScalaCheck & ScalaTest & JUnit

In the example provided by the oneline course, they used JUnit, ScalaTest and ScalaCheck together. In their example, the class of Properties is in src/main/scala and is called by another class defined under src/test/scala. In the class under test folder, many instances of the Properties class are created to check different children classes of the target class.

abstract class QuickCheckHeap extends Properties("Heap") with IntHeap {

  property("min1") = forAll { a: Int =>
    val h = insert(a, empty)
    findMin(h) == a
  }

  lazy val genHeap: Gen[H] = for {
    a <- arbitrary[Int]
    h <- oneOf(value(empty), genHeap)
  } yield insert(a, h)

  implicit lazy val arbHeap: Arbitrary[H] = Arbitrary(genHeap)

}
@RunWith(classOf[JUnitRunner])
class QuickCheckSuite extends FunSuite with Checkers {
  def checkBogus(p: Prop) {
    var ok = false
    try {
      check(p)
    } catch {
      case e: TestFailedException =>
        ok = true
    }
    assert(ok, "A bogus heap should NOT satisfy all properties. Try to find the bug!")
  }
  test("Binomial heap satisfies properties.") {
    check(new QuickCheckHeap with BinomialHeap)
  }
  test("Bogus (1) binomial heap does not satisfy properties.") {
    checkBogus(new QuickCheckHeap with Bogus1BinomialHeap)
  }
  test("Bogus (2) binomial heap does not satisfy properties.") {
    checkBogus(new QuickCheckHeap with Bogus2BinomialHeap)
  }
  test("Bogus (3) binomial heap does not satisfy properties.") {
    checkBogus(new QuickCheckHeap with Bogus3BinomialHeap)
  }
  test("Bogus (4) binomial heap does not satisfy properties.") {
    checkBogus(new QuickCheckHeap with Bogus4BinomialHeap)
  }
  test("Bogus (5) binomial heap does not satisfy properties.") {
    checkBogus(new QuickCheckHeap with Bogus5BinomialHeap)
  }
}

2.1 Tutorial of ScalaCheck

It is a tool for property-based testing for scala. It has NO external dependencies and integrated in the test frameworks ScalaTest. It can also be used standalone, with its built-in test runner. First, create a class that extends class org.scalacheck.Properties with the name of data object that you want to test. It is used for the library to generate test data to test your algorithm.

Second, create test case by

property("NAME_OF_FUNCTION") = forAll{
    CONTAINER_OF_DATA => TEST_CASE_WITH_TARGET_FUNCTION
}

forAll is the function org.scalacheck.Prop.forAll.

Third, to run the tests, you can put the properties in src/test/scala then use test task to check them.

2.2 ScalaTest

This is a test framework that integrates all together. ScalaTest provides many test styles: FunSuit, FlatSpec, FuncSpec, WordSpec, FreeSpec, Spec, PropSpec, FeatureSpec. They are mainly different on syntax styles. It is recommanded that the user creates a base class that extends all the required features (as a subset of all the provided features) and extends this base class through everywhere of the project to make the style consistent.

2.2.1 With JUnit

This class uses the junit framework for unittest. JUnit will invoke the class with @RunWith annotation (extend by type hierarchies) to run the tests. The annotation parameter in the example is a class of the type JUnitRunner. In fact, any class extends Runner is acceptabel. Notice that function classOf acts the same as obj.getClass().

Maven Running tests with command mvn test. In fact, you cannot run the tests with the example above. Solution is to change the name QuickCheckSuite to QuickCheckSuiteTest or TestQuickCheckSuite or QuickCheckSuiteTestCase to run the tests. Even I did not explicitly specify plugin surefire, maven uses this plugin to run test with my configuration. By default, maven following name convertions when looking for tests to run. So I have to change the name or modify the surefire configuration to apply new name convertion rules.

The annotation uses class of JUnitRunner as parameter. This class is provided by scala-test framework that connect scala-test and junit.

  • As mentioned on ScalaTest Maven Plugin, you can run tests without this annotation by using this plugin. TODO

2.2.2 With ScalaCheck

To work with ScalaCheck, you have to mix the trait org.scalatest.prop.Checkers to your test class. Checkers class contains check method to perform ScalaCheck property checks, which are provided in the class QuickCheckHeap in the example above. In fact, you can also use the check method in JUnit directly. This method will generate a large number of possible inputs to look for an input that the property does not hold.

3 Async

3.1 Exception => Future

Exception is handled by Try[T], which is used as return type like Option. But Try[T] maches Success with result or Failure with throwable. As Try[T] is also a monad, operations of element T are usually propagated to monad methods of Try[T] to get a new Try[U], map Try[T] by f should return a new Try[f(T)].

Future[T] is a container of Try[T]. User provide operations/callbacks that deal with Try[T] and future will call them at certain time. Future and Try form a nested monad, monad operations are propagated and you cannot see direct operation on T in the source code of scala library. But take it easy, usually, you won’t have to do it yourself.

To get/compose a future (that can be our job), therer are four methods:

  1. Use the member function of a Future such as flatMap
    1. flatMap, binary bind operation of futures. Register promise to this when failed of exception of call funtion; register promise to that when successed.
    2. onComplete, parameter is a function Try[T] => U. This is a funtamental funciton of map, foreach, failed, etc. It propagate the Try mechanism.
    3. collect
    4. recover
    5. fallbackTo
    6. zip
    7. andThen
  2. Create a promise and register the complete method to current futures, then return a new Future of this promise.

    Promise acts like a mailbox, to compose a new future, the old future has to put the result, which is a Try[T], into the promise by complete method. Then the new future will call its callback when the complete of promise is called.

    In my opinion, promise can be merged with feature as one unit. Because it is more straight to think in the way that: feature calls the callbacks when it is completed. In fact, the DefaultPromise is actually extends the Future trait. Of course, the designers of scala have their proper reason.

    Currently, I think promise is used in race (concurrent processing of futures results) or to provide a new future for initailization of fold operations.

  3. Use for-expression, which is a syntax sugar of flatMap
  4. Use async...await grammar.
    1. The scala implementation of await is translated to onComplete by the macro.
    2. await method throw directly the excepetion when future failed.

3.2 Iterable => Observable

Try and Future are dual, so as Iterable and Observable. For iterable, you can get an iterator and using hasNext and next to get elements. For Obserable, you use Subscribe and onNext, onComplete to get elements.

flatten after map returns an observable that merges several streams randomly. concat after map returns an observable, in which, elements of each stream are grouped together sequentially as well as the order of the streams.

Don’t understand well the way of creating observable with a function from Observer to Subscription -_-!

3.2.1 Subject

It works like promise, which is used as bridge or chain between Obsever and Observable. Four types of subjects are provided:

  1. PublishSubject: send current value.
  2. BehaviorSubject: cache the latest output.
  3. AsyncSubject: cache the final output. You only get the final output.
  4. ReplaySubject: cache the whole history.

3.2.2 Notification

Like Try

3.2.3 Subscription

a handle that enables process to stop receiving messages from Observable.