Thursday, August 29, 2019

Kotlin in Action: Answers to Chapter 11 Exercises

Here's the answers to the chapter 11 exercises.

Exercise: Family Tree

fun man(description: Man.() -> Unit): Man =
    Man().apply(description)

fun woman(description: Woman.() -> Unit): Woman =
    Woman().apply(description)

class Man: Person() {
    var wife: Woman? = null

    fun wife(description: Woman.() -> Unit): Woman =
        woman(description).also { woman ->
            wife = woman
            woman.husband = this
        }

    override fun toString(): String =
        toString("Man", "wife", wife?.name)
}

class Woman: Person() {
    var husband: Man? = null

    fun husband(description: Man.() -> Unit): Man =
        man(description).also { man ->
            husband = man
            man.wife = this
        }

    override fun toString(): String =
        toString("Woman", "husband", husband?.name)
}

sealed class Person() {
    val name = Name()
    var father:Man? = null
    var mother:Woman? = null
    val children = mutableListOf<Person>()

    fun name(description: Name.() -> Unit): Name =
        name.apply(description)

    fun father(description: Man.() -> Unit): Man =
        man(description).also { man ->
            father = man
            man.children.add(this)
        }

    fun mother(description: Woman.() -> Unit): Woman =
        woman(description).also { woman ->
            mother = woman
            woman.children.add(this)
        }

    fun son(description: Man.() -> Unit): Man =
        man(description).also { man ->
            children.add(man)
            man.addParent(this)
        }

    fun daughter(description: Woman.() -> Unit): Woman =
        woman(description).also { woman ->
            children.add(woman)
            woman.addParent(this)
        }

    protected fun addParent(parent: Person) {
        when (parent) {
            is Man -> {
                father = parent
                parent.wife?.let { mother ->
                    this.mother = mother
                    mother.children.add(this)
                }
            }
            is Woman -> {
                mother = parent
                parent.husband?.let { father ->
                    this.father = father
                    father.children.add(this)
                }
            }
        }
    }

    protected fun toString(
        typeName: String,
        spouseTypeName: String,
        spouseName: Name?
    ): String =
        "$typeName(" +
            "name=$name, " +
            "father=${father?.name}, " +
            "mother=${mother?.name}, " +
            "$spouseTypeName=$spouseName, " +
            "children=${children.map {it.name}}" +
        ")"
}

data class Name(var first:String="",
                var middle:String="",
                var last:String="")

Wednesday, August 28, 2019

Kotlin in Action: Chapter 11 Exercises

Chapter 11 is about DSLs, and in addition to going over multiple example DSLs, it also gives us two more tools to use to assist in creating them: lambdas with receivers and the invoke convention. This is a topic that has a lot of depth to it, and we won't be able to cover all possibilities and options in a simple exercise, but with this exercise I hope to cover something with enough complexity to show the power and potential, while also keeping it simple enough to not be overwhelming.

Exercise: Family Tree

With this exercise we'll cover creating a simple family tree DSL. Note that for the sake of simplicity we'll stick with a very basic definition of a family where a father and mother have kids together, even though the definition of family can be much more complex in real life. So in this DSL, we can start by specifying a man or woman, and then in that person's definition a name can be specified, as well as father, mother, son, and daughter. For a man a wife can be specified, and for a woman a husband can be specified. Behind the scenes the DSL should hook up all the connections so that when, say, a father is specified, then the current person is added as a child of the person specified as the father, in addition to the father being added to the current person. So ultimately we should be able to specify code like the following with the DSL:
val john = man {
  name {
    first = "John"
    last = "Smith"
  }
  father {
    name {
      first = "Bob"
      middle = "Bobby"
      last = "Smith"
    }
  }
  mother {
    name {
      first = "Jane"
      last = "Doe"
    }
  }
  wife {
    name {
      first = "Martha"
      middle = "Molly"
      last = "May"
    }
  }
  son {
    name { first = "Joey"; last = "Smith" }
  }
  daughter {
    name { first = "Susie"; middle = "Que"; last = "Smith" }
  }
}

val sally = woman {
  name { first = "Sally" }
  husband {
    mother {
      father {
        name { first = "Richard" }
      }
    }
  }
}
As usual, answers to the exercise will be shared in a follow up post. And here's the answers.

Wednesday, August 21, 2019

Kotlin in Action: Answers to Chapter 10 Exercises

Here's the answers to the chapter 10 exercise.

Exercise: Basic Dependency Injection

So here's the implementation of the build method:
val objMap = mutableMapOf<KClass<*>, Any>()

inline fun <reified T: Any> build() =
  build(T::class)

@Suppress("UNCHECKED_CAST")
fun <T: Any> build(klass: KClass<T>): T {
  if (objMap.containsKey(klass)) {
    return objMap[klass] as T
  } else {
    with(klass.constructors.first()) {
      val paramObjs = parameters
        .map { build(it.type.classifier as KClass<*>) }
        .toTypedArray()
      val obj = call(*paramObjs)
      objMap[klass] = obj
      return obj
    }
  }
}
For the bonus exercise of adding a @Singleton annotation, we'd want to define the annotation like this:
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Singleton
And then rework the build method like this:
@Suppress("UNCHECKED_CAST")
fun <T: Any> build(klass: KClass<T>): T {
  if (objMap.containsKey(klass)) {
    return objMap[klass] as T
  } else {
    with(klass.constructors.first()) {
      val paramObjs = parameters
        .map { build(it.type.classifier as KClass<*>) }
        .toTypedArray()
      val obj = call(*paramObjs)
      if (klass.findAnnotation<Singleton>() != null) {
        objMap[klass] = obj
      }
      return obj
    }
  }
}
With this code in place, if you run our example with Nodes 1 through 4, you'll notice that the Node1 ids are different when it doesn't have the @Singleton annotation, but they are the same if you annotate the Node1 class with @Singleton.

Tuesday, August 20, 2019

Kotlin in Action: Chapter 10 Exercises

Chapter 10 covers annotations and reflection. To get a better grasp on this, we'll implement a very basic dependency injection framework using reflection.

Basic Dependency Injection

We'll implement a very bare bones dependency injection framework. With it you will be able to supply a class to a build method, and it will instantiate that class, as well as its dependencies.We'll limit what scenarios it will work with to simplify the problem. All classes are treated as singletons, and only a single instance of each class will be created. If a class requests a dependency that has already been instantiated, then it will be reused. We're only requiring this to work with Kotlin classes, so Java classes don't need to be considered. It'll also be assumed that the classes will have a single constructor which defines the class dependencies. We also won't handle dependencies where there could be multiple different instances of the class (so no String, Number, or other such dependencies). We also won't worry about detecting circular dependencies.

So as such, if we have classes that look like this:
class Node1
data class Node2(val node1: Node1)
data class Node3(val node1: Node1, val node2: Node2)
data class Node4(val node3: Node3)
And ran this code:
fun main() {
  val node4 = build<Node4>()
  println(node4)
}
We should get something like the following printed to the console (note that the Node1 Identifiers need to match):
Node4(node3=Node3(node1=Node1@18d87d80, node2=Node2(node1=Node1@18d87d80)))
Also note that since kotlin reflection is in a different jar, you'll need to add the following to your build.gradle file:
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.41"
As a bonus activity, after we've completed this we can modify it and add a Singleton annotation, and make it so that the class instance reference is only kept and reused if the class is annotated as Singleton, otherwise it will create a new instance each time it is needed. This will allow us to play with defining and referencing annotations.

As usual, the answers will be posted in a followup post. And here's the answers.

Thursday, August 15, 2019

Kotlin in Action: Answers to Chapter 9 Exercises

Here's the answers to the chapter 9 exercises.

Exercise 1: Covariance and Contravariance

So similar to how Java uses the keywords extends and super to define covariance and contravariance, Kotlin uses the keywords out and in, respectively. In a way, this makes more sense, since covariance is usually applied when producing a type, and contravariance is applied when consuming a type. In Java, the acronym PECS for "Producer Extends Consumer Super" is usually used to remember this and keep things straight, whereas is Kotlin the association is intuitive. The out keyword is used when producing, and the in keyword is used when consuming. So in Kotlin, we can create the addDogs method like this:
fun addDogs(list: MutableList<in Dog>,
            toAdd: List<out Dog>) {
  list.addAll(toAdd)
}
Though note that with this method, many IDEs, like Intellij, will give you a warning on the out keyword that says something like this: "Projection is redundant: the corresponding type parameter of List has the same variance." This is because Kotlin allows for declaration site variance, and since in Kotlin the List type is immutable (which in turn means that it has no methods that consume its type, only methods that produce), the List type is already marked as covariant. If you look at List's definition, you'll see something like this:
public interface List<out E> : Collection<E> {
  ...
}
So since the List type already declared the variance, we don't need to, which allows up to remove the out keyword and gives us this:
fun addDogs(list: MutableList<in Dog>,
            toAdd: List<Dog>) {
  list.addAll(toAdd)
}
Do note that if we had declared the toAdd parameter as a MutableList, then it would have required the out keyword. This is because it has both producer methods (like the get method) and consumer methods (like the add method). As such, it is neither covariant nor contravariant, and is instead invariant, which means that only the specific type can be used (if a list of dogs is requested, neither a list of animals, nor a list of poodles, would be acceptable; only a list of dogs).

But of course, just because a type is invariant, it doesn't mean that it can't be used in a covariant or contravariant way, it just means that it requires use site variance, which is why the in keyword is required on the MutableList in our addDogs function.

Now as a final bonus, let's convert the addDogs function to be an extension function. It can be done like this:
fun MutableList<in Dog>.addDogs(toAdd: List<Dog>) {
  addAll(toAdd)
}
And it can be used like this:
animals.addDogs(dogs)
dogs.addDogs(dogs)
animals.addDogs(chihuahuas)
dogs.addDogs(chihuahuas)
animals.addDogs(poodles)
dogs.addDogs(poodles)

Exercise 2: Reified Types

Well the explanations around the last example were a little long winded, but the example with reified types should be pretty straightforward. In short, reified types can be used in ways that a normal type can't, but reified types can only be used in inline functions. So to create our cast method, we'd want to do something like this:
inline fun <reified T> Observable<*>.cast() =
  cast(T::class.java)

Kotlin in Action: Chapter 9 Exercises

Chapter 9 covers generics, including topics such as invariance, covariance, contravariance, inlining, and reified types. We'll go over two different exercises to help us better understand some of these points in Kotlin.

Exercise 1: Covariance and Contravariance

To better understand covariance and contravariance, we'll start by creating an easy to understand hierarch of types:
open class Animal
open class Dog: Animal()
class Chihuahua: Dog()
class Poodle: Dog()
open class Cat: Animal()
class Persian: Cat()
class Birman: Cat()
So in this structure, the top level is Animal. A Dog is an Animal, and a Cat is an Animal. A Chihuahua is a Dog (which in turn is an Animal), and same goes for a Poodle. Persian and Birman are both Cats.

With this type hierarchy, we can create lists that can contain different categories of animals:
val animals = mutableListOf<Animal>()
val dogs = mutableListOf<Dog>()
val cats = mutableListOf<Cat>()
val chihuahuas = mutableListOf<Chihuahua>()
val poodles = mutableListOf<Poodle>()
val persians = mutableListOf<Persian>()
val birmans = mutableListOf<Birman>()
Now what we want to do is create a function called addDogs that will take in two lists. The first list should be any list that any kind of dog can be added to (either a list of animals or a list of dogs would fit the bill). The second list should be a list that is guaranteed to only have dogs in it (in this case, either a list of dogs, a list of chihuahuas, or a list of poodles would work). This function will take all the dogs in the second list and add them to the first list. Such a function would look like this in Java:
public void addDogs(List<? super Dog> list,
                    List<? extends Dog> toAdd) {
    list.addAll(toAdd);
}
This would in turn allow for the following legal uses:
addDogs(animals, dogs)
addDogs(dogs, otherDogs)
addDogs(animals, chihuahuas)
addDogs(dogs, chihuahuas)
addDogs(animals, poodles)
addDogs(dogs, poodles)
While the following scenarios won't compile:
addDogs(chihuahuas, dogs) //chihuahuas can't accept any kind of dog
addDogs(dogs, animals) //animals might contain cats
addDogs(cats, persians) //while cats can hold persians, neither fullfill the contract,
                        //cats can't accept dogs, and persians doesn't contain dogs
So can we write the addDogs method in Kotlin? As a bonus activity, we can also explore converting it into an extension function.

Exercise 2: Reified Types

For this exercise, we'll use the RxJava2 library, so you'll want to add the following to your gradle file:
compile group: 'io.reactivex.rxjava2', name: 'rxjava', version: '2.2.11'
We'll also make use of the same animal classes from the above example, but we'll add a bark method to the Dog class, for illustrative purposes:
open class Dog: Animal() {
  fun bark() {
    println("bark")
  }
}
Now RxJava has a cast operator so that you can cast an object from one type to another in its chain. For instance, if you have an animal that you know is a Poodle and you need to cast it to Dog so that you can all the bark function on it:
val animal = Poodle() as Animal
Observable.just(animal)
  .cast(Dog::class.java)
  .subscribe{ dog -> dog.bark() }
Now the fact that the cast method takes in a Java class parameter makes it kind of long and ugly. Let's see if we can write an extension function that would allow us to do this instead:
val animal = Poodle() as Animal
Observable.just(animal)
  .cast<Dog>()
  .subscribe{ dog -> dog.bark() }
This will require us to make use of reified types.

As always, the answers will be given in a follow up post. And here's the answers.

Wednesday, August 7, 2019

Kotlin in Action: Answers to Chapter 8 Exercises

Here's the answer to the chapter 8 exercise.

Exercise: Filter Out

So first off, we want to be able to do this:
listOf(1, 2, 3, 4, 5)
  .filterOut { it == 2 }
Where the result would be a list of [1, 3, 4, 5] where 2 was filtered out, because it matched the predicate in the filterOut method. To do this, we can do the following:
public inline fun  Iterable.filterOut(predicate: (T) -> Boolean) =
  filter { !predicate(it) }
Note that we we were able to mark this function as inline, so in essense, when the code compiles, it ends up being equivalent to if you had written:
listOf(1, 2, 3, 4, 5)
  .filter { !(it == 2) }
Though that's actually a lie, because the filter method itself is inline, and as such when the code compiles, it's more like this:
val source = listOf(1, 2, 3, 4, 5)
val destination = ArrayList<Int>()
for (element in source) if (!(element == 2)) destination.add(element)
So you get the speed and efficiency benefits as if you had actually written the code like this, while still getting the niceness how how the code was written above.

But we're not done with this example just yet, because as we learned in chapter 5, you can also do the same operations with a sequence. A sequence will simply try to optimize all the operations being performed to a collection, and our solution wouldn't be complete without having a filter out method for Sequence. So we ultimately want to be able to do the following:
listOf(1, 2, 3, 4, 5)
  .asSequence()
  .filterOut { it == 2 }
  .toList()
To do this, we'll want to write the following code:
public fun  Sequence.filterOut(predicate: (T) -> Boolean) =
  filter { !predicate(it) }
It's nearly identical to the filter out method that we wrote for the Iterable, but you'll notice that we didn't use inline here. That's because we can't inline this function, because the filter function on the Sequence is not inlined. If you try to put inline on this function, you will get a compile time error. The reason why the filter function is not inline on the sequence is so that the sequence has the flexibility to optimize the order in which different calls are made in order to minimize the amount of work that needs to be done when processing large data sets.

Now if you really had your heart set on inlining this function, you could do the following:
public inline fun  Sequence.filterOut(noinline predicate: (T) -> Boolean) =
  filter { !predicate(it) }
Which essentially tells the compiler that the lambda being passed down into the filter method shouldn't be inlined. Now while this can be useful in a number of situations, in this particular case it really isn't all that useful, because it won't be able to eliminate any of the previously mentioned overhead. After compilation, it's essentially as if you had written the following code:
listOf(1, 2, 3, 4, 5)
  .asSequence()
  .filter { !(it == 2) }
  .toList()
And it can't optimize any further past that because the filter function is not inlined.

And so with that, the final solution is this:
public inline fun  Iterable.filterOut(predicate: (T) -> Boolean) =
  filter { !predicate(it) }

public fun  Sequence.filterOut(predicate: (T) -> Boolean) =
  filter { !predicate(it) }

Kotlin in Action: Chapter 8 Exercises

Chapter 8 covers higher order functions, which is just a fancy way of saying functions that can take a function as a parameter or return a function (or both). it also covers other points such as inline functions, non local returns, and many other such points.

Exercise: Filter Out

So for a simple exercise to test our knowledge out, let's create a method that takes in a lambda. We'll create a filterOut method, which works similarly to the filter method, but instead of keeping anything that matches the predicate, it will remove anything that matches the predicate, and keep everything else.

As usual, an answer will be given in a follow up post. And the answer can be found here.

Thursday, August 1, 2019

Kotlin in Action: Answers to Chapter 7 Exercises

Here's the answer to the chapter 7 exercise.

Exercise: Operator Overloads for Json Manipulation

Immutable

operator fun JsonObject.plus(other: JsonObject): JsonObject =
  copy().apply {
    other.forEach { (key, value) -> put(key, value) }
  }

operator fun JsonObject.plus(pair: Pair<String, *>): JsonObject =
  copy().put(pair.first, pair.second)

operator fun JsonObject.minus(key: String): JsonObject =
  copy().apply { remove(key) }

operator fun JsonObject.minus(keys: Collection<String>): JsonObject =
  copy().apply {
    keys.forEach { remove(it) }
  }

operator fun JsonArray.plus(other: JsonArray): JsonArray =
  copy().addAll(other)

operator fun JsonArray.plus(item: Any?): JsonArray =
  copy().add(item)

operator fun JsonArray.minus(other: JsonArray): JsonArray =
  copy().apply {
    other.forEach { remove(it) }
  }

operator fun JsonArray.minus(item: Any?): JsonArray =
  copy().apply { remove(item) }

operator fun JsonArray.minus(index: Int): JsonArray =
  copy().apply { remove(index) }

Mutable

operator fun JsonObject.plusAssign(other: JsonObject) =
  other.forEach { (key, value) -> put(key, value) }

operator fun JsonObject.plusAssign(pair: Pair<String, *>) {
  put(pair.first, pair.second)
}

operator fun JsonObject.minusAssign(key: String) {
  remove(key)
}

operator fun JsonObject.minusAssign(keys: Collection<String>) =
  keys.forEach { remove(it) }

operator fun JsonArray.plusAssign(other: JsonArray) {
  addAll(other)
}

operator fun JsonArray.plusAssign(item: Any?) {
  add(item)
}

operator fun JsonArray.minusAssign(other: JsonArray) {
  other.forEach { remove(it) }
}

operator fun JsonArray.minusAssign(item: Any?) {
  remove(item)
}

operator fun JsonArray.minusAssign(index: Int) {
  remove(index)
}