Showing posts with label Kotlin. Show all posts
Showing posts with label Kotlin. Show all posts

Friday 23 September 2022

Kotlin: abstract classes, backing fields/properties, lateinit, and frustration

G'day:

I'm just continuing on in the Kotlin "Classes" docs, carrying on from my earlier article "Kotlin: investigating classes".


Abstract classes

I have pretty much run with the example in the docs just to see it in action. I don't wanna copy and paste stuff cos I don't learn so well just by looking at the code. Making myself remember what I need to type - even if it's just class syntax with a method or two - helps me learn it.

The thing the example had was to have an abstract class that has a concrete implementation of a method, extended by another abstract class that actually makes that method abstract again, followed by a concrete implementation class. To get my brain around that, I wrote it in Java first (all this experimentation is making my Java better too btw: I'm writing more Java-comparison code for these articles than I'm necessarily showing in them). My Java is limited to one-off class files with a main method though. I can't be arsed setting up a Java project in IntelliJ and working out how to get JUnit working, etc. Sorry. (Dammit I feel guilty now). Anyway, Java version:

public class TestAbstract {
    public static void main(String[] args){
        MyWildShape mws = new MyWildShape();
        mws.draw();
    }
}

abstract class Shape {
    public void draw() {
        System.out.println("Shape.draw"); // MyWildShape.draw
    }
}


abstract class WildShape extends Shape {
    abstract public void draw();
}  


class MyWildShape extends WildShape {
    @Override
    public void draw() {
        System.out.println("MyWildShape.draw");
    }
}

And the Kotlin version:

abstract class Shape {
    open fun draw() = print("Shape.draw")
}

abstract class WildShape : Shape() {
    abstract override fun draw()
}

class MyWildShape : WildShape() {
     override fun draw() = print("MyWildShape.draw")
}

class AbstractTest : DescribeSpec({
    describe("abstract test") {
        it("permits concrete subclasses to override abstract methods that are not overridden in the base class") {
            val mws = MyWildShape()
            val output = SystemLambda.tapSystemOut {
                mws.draw()
            }
            output shouldBe "MyWildShape.draw"
        }
    }
})

I wondered what the need for open was in that. Kotlin's been pretty good an letting boilerplate be inferrable, and this seemed like boilerplate. I found the answer in the docs:

By default, Kotlin classes are final – they can't be inherited. To make a class inheritable, mark it with the open keyword

Kotlin / Concepts / Classes and objects / Inheritance

As to why that's the case, I had to dig slightly deeper, but I found a discussion on it here: Classes final by default, and at the top of that discussion someone links to the book "Effective Java" wherein I guess someone recommends making stuff final by default. I'm not entirely convinced with this as it kinda presupposes all code is written in a specific way: implemented to an interface, and nothing every expects a concrete implementation, basically. I'm OK writing my code like that, however there's no guarantee the author of some lib I need to use also has. And in my experience (granted: in PHP) it's not the case. But so be it. I guess it does encourage one to stop and think. And design to interfaces. Which is a good thing.


Companion objects

I messed with these in a coupla other articles:

I've nothing else I feel I need to check-out on these right now.


Back to backing fields (and backing properties)

Fields

In an earlier article ("Kotlin: there's no such thing as static, apparently"), I learned that a property has a backing field: the underlying variable that stores the value. One just refers to it as field in accessor implementations:

class Person {
    var firstName = ""
        get() = field
        set(value) {
            field = value
        }
    var lastName = ""
        get() = field
        set(value) {
            field = value
        }
}

Under the hood Kotlin is creating appropriately-named discrete private variables for each property's field.

One cool thing IntelliJ can do is to show the bytecode that the Kotlin code would generate (Tools › Kotlin › Show Kotlin Byte Code), and from there one can decompile that back to Java. The result in this case being (I've trimmed some irrelevant stuff):

final class Person {
   private String firstName = "";
   private String lastName = "";

   public final String getFirstName() {
      return this.firstName;
   }

   public final void setFirstName(String value) {
      this.firstName = value;
   }

   public final String getLastName() {
      return this.lastName;
   }

   public final void setLastName(String value) {
      this.lastName = value;
   }
}

One thing I wondered about before when I found myself getting a stack overflow when doing this:

var firstName = ""
    get() = field
    set(value) {
    	// was field = value
        firstName = value
    }

Whether this was because my reference to firstName there was actually calling "itself". It turns out it is, and IntelliJ was even telling me:


Backing properties

This was the next thing that threw me. The docs also talk about backing properties, and really didn't make it at all clear why these existed.

If you want to do something that does not fit into this implicit backing field scheme, you can always fall back to having a backing property

They give an example, and I could understand the code, but it didn't really sink in as to what specifically showing me. I did a google and was pleased to find out I was not the only person a bit flumoxed by all this, and found a good answer here: I have a hard time understanding the purpose of a "backing property". And I was able to contrive a test case to demonstrate it (largely to myself) in action:

class MyList {
    private var _myList = mutableListOf<String>()
    val immutable: List<String>
        get() = _myList
    var mutable: MutableList<String> = mutableListOf()
        set(value) {
            field = value
            _myList = value
        }
}

There's really no trick to it. It's just when an accessor method needs to store its value in some other variable, not its backing field. In the example above I have a class that has a property called mutable that has a setter which will take a MutableList of strings, and another property called immutable which actually accesses the same underlying variable, but returns it as an immutable List. This is a daft example, but it demonstrates the point. I've two properties, and they both access the same underlying data, so it has to be in a specific private property rather than using field which intrinsically is a different variable for each property. Also note I am setting mutable's backing field too. Not for any reason, just to show I can.

Of course I wrote a test for this:

class BackingPropertiesTest : DescribeSpec({
    it("should take a MutableList and return an ImmutableList") {
        val mutableList = mutableListOf("tahi", "rua", "toru")
        val myList = MyList()
        myList.mutable = mutableList

        myList.immutable.shouldBeInstanceOf<List<String>>()
        myList.immutable shouldBe listOf("tahi", "rua", "toru")

        mutableList.add("wha")
        myList.immutable.shouldBeInstanceOf<List<String>>()
        myList.immutable shouldBe listOf("tahi", "rua", "toru", "wha")
    }
})

Straight forward stuff: just demonstrating what the code is supposed to be doing. One thing I thought was neat was how I could append that new element to the mutable list, and when I accessed it again, I got a new immutable list out, also with my new value. It almost seems like I am changing the immutable list, but of course it's a new list each time.


lateinit

This is the solution to a problem that I had to work around the other day. In the end I was "just doing it wrong" for the given situation (I forget what it was now), but I had thought "but I don't want to initalise that with a non-null value now just to satisfy the compiler. I'll deal with it later. Trust me". And of course the "trust me" part is implemented via the lateinit modifier.

class Colour(val en: String) {
    lateinit var mi: String

    fun setMaori(value: String) {
      mi = value
    }
}

Here I have then English version of the colour as a normal property handled by the primary constructor, and another property mi representing the Maori word for the colour, and that isn't handled by any constructor, and is also not initialised. It's happy to stay undefined until "later". If I didn't have the lateinit there, the code would not even compile:

e: [...]\LateInitPropertiesTest.kt: (52, 5): Property must be initialized or be abstract

IntelliJ makes it clear too:

I have a coupla tests demonstrating this in action:

it("allows me to create a colour object without an mi property") {
    val red  = Colour("red")
    red.en shouldBe "red"
}

it("lets me set the mi property later") {
    val orange  = Colour("orange")
    orange.setMaori("karaka")

    orange.mi shouldBe "karaka"
}

What if I try to read that property before it's been initialised?

it("throws UninitializedPropertyAccessException if access mi before it's been set") {
    val yellow  = Colour("yellow")

    val exception = shouldThrow<UninitializedPropertyAccessException> {
        yellow.mi shouldNotBe "kōwhai"
    }
    exception.message shouldBe "lateinit property mi has not been initialized"
}

It throws an exception at runtime. Cool. And it's a specific exception, with a clear message.

For good measure I also checked with a method call with the reference to mi within the class. I did not expect any different behaviour, but I like to check these things.

class Colour(val en: String) {
    lateinit var mi: String

    fun setMaori(value: String) {
      mi = value
    }

    fun getMaori() = mi
}
it("throws UninitializedPropertyAccessException if use mi before it's been set") {
    val green  = Colour("green")

    val exception = shouldThrow<UninitializedPropertyAccessException> {
        green.getMaori() shouldNotBe "kakariki"
    }
    exception.message shouldBe "lateinit property mi has not been initialized"
}

One can also check to see if the property is initialised before using it:

class Colour(val en: String) {
    lateinit var mi: String

    fun setMaori(value: String) {
      mi = value
    }

    fun getMaori() = mi

    fun isMiInitialized() = ::mi.isInitialized
}
it("can have initialisation status checked") {
    val green  = Colour("green")

    green.isMiInitialized() shouldBe false
    green.setMaori("kakariki")
    green.isMiInitialized() shouldBe true
}

I thought I might be able to go green::mi.isInitialized, but I get told off:

Looking in the docs, this is because:

This check is only available for properties that are lexically accessible when declared in the same type, in one of the outer types, or at top level in the same file.

Ooookay. I cannot actually work out how to write code where I can call isInitialized where the property is "declared… in one of the outer types, or at top level in the same file". I always get "Backing field of 'var someValue: String' is not accessible at this point". And that is a coupla hours or so of my life I am never going to get back.


And as that was a bit frustrating at the end there, I'm gonna go and do something else now. You need to picture me with my bottom lip jutted-out in a put-upon way when I say that. I tell you: if I had a ball, I would take it and go home ;-)

Righto.

--
Adam

Monday 19 September 2022

Kotlin: investigating classes

G'day:

In my other random explorations of the Kotlin language, I've already used simple classes to facilitate testing other language features, but my next koans exercise (man I am progressing slowly with those!) links to the docs page for classes, so I'm gonna have a breeze through that, so I can extend my understanding of how they operate beyond the superficial level I currently have. Anything that intrigues me or is non-obvious, I'll write a test and sling it in here.


Primary and secondary constructors

I touched on this in "Kotlin: another Friday afternoon, another round of random investigation › Primary and secondary constructors", but only in as much as I messed up my syntax for my constructor and IntelliJ fixed it for me.

Primary constructor

This is baked-into the class declaration:

class Suffragist(firstName:String, lastName:String) {
    val fullName = "$firstName $lastName"
}

class ClassesTest : DescribeSpec({
    describe("Constructor tests") {
        it("takes values that can be used in initialisation code") {
            val suffragist = Suffragist("Kate", "Sheppard")

            suffragist.fullName shouldBe "Kate Sheppard"
        }
    }
})

It's important to understand that the primary constructor here is just the bit in parentheses. The braces are not the constructor implementation, that is the class implementation, for example in Java that constructor (in context) would be:

class Suffragist {

    String firstName;
    String lastName;

    public Suffragist(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

So the primary constructor identifies the properties needed for initialisation of the object.

In my class above, firstName and lastName are not properties of the Suffragist object. In my test I could not do this:

suffragist.firstName shouldBe "Kate"
suffragist.lastName shouldBe "Sheppard"

This gives a compile error:

Unresolved reference: firstName

To make them properties, I need to put a val or var qualifier in the primary constructor:

class Scientist(val firstName:String, val lastName:String)

class ClassesTest : DescribeSpec({
    describe("Constructor tests") {
        // ...
        it("needs to qualify params to make them properties") {
            val scientist = Scientist("Siouxsie", "Wiles")

            scientist.firstName shouldBe "Siouxsie"
            scientist.lastName shouldBe "Wiles"
        }
    }
})

Back to the first example:

class Suffragist(firstName:String, lastName:String) {
    val fullName = "$firstName $lastName"
}

That line is not part of the constructor, it's initialising the fullName property. It just happens to have access to the firstName and lastName values from the constructor when it's executed.


Initializer blocks

As the primary constructor can't have any code in it, any code that needs to run when the object is created needs to be in initializer blocks in the class body:

class Politician(firstName:String, lastName:String) {
    init {
        println("First name: $firstName")
    }
    init {
        println("Last name: $lastName")
    }
}

class ClassesTest : DescribeSpec({
    describe("Constructor tests") {
        
        // ...
        
        it("can have code in initializer blocks") {
            val EOL = System.lineSeparator()
            val output = SystemLambda.tapSystemOut {
                Politician("Jacinda", "Ardern")
            }

            output shouldBe "First name: Jacinda${EOL}Last name: Ardern${EOL}"
        }
    }
})

Unsurprisingly they are executed in the order they are in the file.


Secondary constructors

Kotlin can have secondary constructors if one needs to do more than take some arguments and pass them to initializer blocks. Secondary constructors use the constructor keyword to define their behaviour.

Here I have a secondary constructor that takes a map of firstName / lastName pairs, and this extracts the values from the map for each of the firstName / lastName parameters of the primary constructor.

class FilmMaker(firstName:String, lastName:String) {
    val fullName = "$firstName $lastName"
    constructor(names: Map<String, String>) : this(names["firstName"]!!, names["lastName"]!!)
}

class ClassesTest : DescribeSpec({
    describe("Constructor tests") {

        // ...
        
        it("can have a secondary constructor") {
            val names = mapOf(Pair("firstName", "Jane"), "lastName" to "Campion")
            val filmMaker = FilmMaker(names)

            filmMaker.fullName shouldBe "Jane Campion"
        }
    }
})

It's important to note that because I have a primary constructor defined, then ultimately one of the secondary constructors must call it, even if there's a sequence of secondary constructors calling one another in a chain, ultimately the primary constructor must be called.

I did wonder why it's not possible to do this sort of thing:

constructor(names: Map<String, String>) {
    val firstName = names["firstName"]
    val lastName = names["lastName"]

    this(firstName!!, lastName!!)
}

IE: to have the secondary constructor do some work and then call the primary constructor. I could not find an answer for this.

BTW, that !! operator is a not-null assertion operator. In this usage it tells the compiler to error-out if the map doesn't have the key/value pair I'm using there. Otherwise I'd need to make the following code handle nulls for those values, and that is incorrect.


Here's an example where there's no primary constructor, which enables a secondary constructor to do more than simply invoke the primary one, inline:

class ChiefJustice {
    var firstName = ""
    var lastName = ""
    var fullName = ""

    constructor(firstName:String, lastName:String) {
        this.firstName = firstName
        this.lastName = lastName

        fullName = "$firstName $lastName"
    }

    constructor(names: List<String>) : this(names.first(), names.last())
}

class ClassesTest : DescribeSpec({
    describe("Constructor tests") {

        // ...

        it("can call another secondary constructor") {
            val names = listOf("Helen", "Winkelmann")
            val chiefJustice = ChiefJustice(names)

            chiefJustice.fullName shouldBe "Helen Winkelmann"
        }
    }
})

Here we have two secondary constructors. One takes separate String values for the properties, and the second takes a list of names, and extracts first and second to pass to the previous secondary constructor to actually then do the initialisation work.


Companion objects and factory methods

I touched on companion objects before in my "Kotlin: there's no such thing as static, apparently" article. One thing I read a few times when reading about secondary constructors, is that they're a bit frowned-up for situations where there's constructors taking different repesentations of the property data values (like all the examples I offer here!). Instead of having a constructor that takes a map of name/value pairs, one would instead have a factory method, using a companion object. So instead of this example:

class FilmMaker(val firstName:String, val lastName:String) {
    val fullName = "$firstName $lastName"
    constructor(names: Map<String, String>) : this(names["firstName"]!!, names["lastName"]!!)
}

One might better have:

class FilmMaker(firstName:String, lastName:String) {
    val fullName = "$firstName $lastName"

    companion object Factory {
        fun createFromMap(names: Map<String, String>) = FilmMaker(names["firstName"]!!, names["lastName"]!!)
    }
}

class ClassesTest : DescribeSpec({
    describe("Constructor tests") {
        it("can have a secondary constructor") {
            val names = mapOf(Pair("firstName", "Jane"), "lastName" to "Campion")
            val filmMaker = FilmMaker.createFromMap(names)

            filmMaker.fullName shouldBe "Jane Campion"
        }
    }
})

Now I have a factory method createFromMap which is a bit more clear in its intent / purpose than having multiple vague constructors.


I'm gonna leave it there today. There's some more sections on that docs page regarding inheritance and abstract classes and stuff, but I'm not in the mood for that right now. Am struggling a bit with focus just now: not quite sure why.

The code for today is on GitHub as ClassesTest.kt (1.5 tag) except the last example which was a refactor of the earlier code, and is tagged as 1.5.1: ClassesTest.kt.

Righto.

--
Adam

Thursday 15 September 2022

Kotlin: scope functions (mostly)

G'day:

More Kotlin learning/investigation, following on from these other Kotlin articles. This time I didn't even get to start the next koans exercise before being sidetracked.


Scope functions

There's a bunch of functions - let, with, run, apply, and also - that one calls on an object; they each take a lambda, and within the lambda expressions act on the object either via it (a parameter of the lambda), or this (a reference to the object), or simply implicitly (no reference to the object at all!). They all seem to do similar stuff - the docs even say so - they're all just variants that are possibly better-suited to one use case or another. I have written a test for each of them, keeping the "feature" being tested similar across all of them to show the differences.

let

it("provides an object to run the statements in the lambda with") {
    val result = listOf(
        Colour("whero"),
        Colour("karaka"),
        Colour("kōwhai")
    ).first()
    .let {
        it.english = "red"
        "${it.maori}: ${it.english}"
    }
    result shouldBe "whero: red"
}

The intent of let is to be able to chuck it in a chain of other method calls. In this contrived example I have a list of colours, I get the first one, and I call let on that. The let lambda just fills in the English value, and returns a string of "[Maori]: [English]" for that colour.


with

it("calls the statements in the lambda with the given object") {
    val green = Colour()

    with(green) {
        maori = "kākāriki"
        english = "green"
    }
    "${green.maori}: ${green.english}" shouldBe "kākāriki: green"
}

with is for when one wants to go "with this object, do some stuff to it". Here the statements are applied to this (the argument of with), implicitly. I could explicitly say this.maori = "kākāriki". I also found the term for those functions that are just functions (doSomething(someObject)), rather than being used like how I'd expect a method to be used: someObject.doSomething(). The term is "non-extension function".

It can also return a value (as per usual with lambdas in Kotlin: the last expression is returned):

it("will return something") {
    val purple = with(Colour()) {
        maori = "papura"
        english = "purple"

        this
    }
    "${purple.maori}: ${purple.english}" shouldBe "papura: purple"
}

For the return, I need to be explicit about this otherwise Kotlin won't know I mean to return it.

run

This is like a combo of with and let: it's an extension function, so can be called in a chain, but it receives the value of the chain as its (implicit) this:

it("applies the statements in the lambda to the object") {
    val blue = listOf(
        Colour("whero"),
        Colour("kākāriki"),
        Colour("kikorangi")
    ).last()
    .run {
        english = "blue"

        this
    }
    "${blue.maori}: ${blue.english}" shouldBe "kikorangi: blue"
}

This is similar to the let example except no need to use "it" when referencing the object.


apply

describe("Tests of apply function") {
    it("applies the assignments in the lambda to the object") {
        val yellow = Colour().apply {
            maori = "kōwhai"
            english = "yellow"
        }
        "${yellow.maori}: ${yellow.english}" shouldBe "kōwhai: yellow"
    }
}

apply seems like an extension function (look at me with my new jargon) version of with. It implicitly returns the original object though. So - and I'm just paraphrasing the docs here - it's idiomatic usage seems to be for configuring an object. One is applying the assignemnts in the lambda to the object.


also

it("also runs the statements in the block with the object") {
    val indigo = Colour().also {
        it.maori = "tūāuri"
        it.english = "indigo"
    }
    "${indigo.maori}: ${indigo.english}" shouldBe "tūāuri: indigo"
}

I'm just gonna quote the docs for this one, as it describes it kinda like how it occurred to me to do so:

When you see also in the code, you can read it as "and also do the following with the object."

Cos it's an extension function, it too could be slung in the middle of some other chain of calls. NB: it implicitly returns the original object.


Callable references

Something cool that the docs used which I will repeat here is passing the object to a callable reference to a function:

it("can use a callable reference to use a function as the callback") {
    val orange = Colour("karaka", "orange")

    val output = SystemLambda.tapSystemOut {
        "${orange.maori}: ${orange.english}".also(::print)
    }

    output shouldBe "karaka: orange"
}

See how I'm passing my string to also, and the lambda is passing it to a reference to print. One would not do this in real life, but it demonstrates the point. If one prefixes a function with the :: operator, one gets a callable reference to that function.


takeIf and takeUnless

The "Scope functions" page of the docs has these two tacked onto the end of it, so I'll do the same.

takeIf

it("returns the object if the predicate is true, otherwise null") {
    var number = 210

    var checked = number.takeIf { it % 2 == 0 }
    checked shouldBe number

    checked = checked?.takeIf { it % 3 == 0 }
    checked shouldBe number

    checked = checked?.takeIf { it % 5 == 0 }
    checked shouldBe number

    checked = checked?.takeIf { it % 7 == 0 }
    checked shouldBe number

    checked = checked?.takeIf { it % 11 == 0 }
    checked.shouldBeNull()
}

The test case kinda explains what this one does. And I admit the actual test is a bit daft.

Note how I need to use the null-safe operator after the first takeIf expression, as the object could be null after that. The code won't compile unless I do. Indeed IntelliJ tells me when I've got it wrong:

(I am really loving the static type-checking in Kotlin, btw).


takeUnless

takeUnless works the other way around: it returns the object if the predicate is false (otherwise null). Why have both? Sometimes the code just reads better with different wording.

it("returns the object if the predicate is false, otherwise null") {
    val number = 210

    number.asClue {
        var checked = it.takeUnless { it % 2 != 0 }
        withClue("should be a multiple of 2") {
            checked shouldBe it
        }

        checked = checked?.takeUnless { it % 3 != 0 }
        withClue("should be a multiple of 3") {
            checked shouldBe it
        }

        checked = checked?.takeUnless { it % 5 != 0 }
        withClue("should be a multiple of 5") {
            checked shouldBe it
        }

        checked = checked?.takeUnless { it % 7 != 0 }
        withClue("should be a multiple of 7") {
            checked shouldBe it
        }

        checked = checked?.takeUnless { it % 11 != 0 }
        withClue("should NOT be a multiple of 11") {
            checked.shouldBeNull()
        }
    }
}

For a change here I'm using those asClue and withClue constructs that Kotest has, as mentioned in the previous article. To recap: this just adds context and hinting to test failures. Let me break a condition there to show you. I've slung this in as the last condition in that previous test:

checked = checked?.takeUnless { it % 13 != 0 }
withClue("should be a multiple of 13") {
    checked shouldBe it
}

And this yields:

210
java.lang.AssertionError: 210
should be a multiple of 13
Expected 210 but actual was null

And that was the bottom of that page of docs, and this is long enough, so I'll leave it here. I suspect I will be back tomorrow with more…

All the code shown here is on Github: /src/test/kotlin/language/ScopeFunctionsTest.kt

Righto.

--
Adam

Saturday 10 September 2022

Kotlin: looking at Numbers, more Kotest stuff, and something about "function literals with receivers"?

G'day:

Here's some new unguided Kotlin experimentation, following on from other similar articles.

Firstly I inadvertantly finished yesterday's article when I was sitting on the last exercise in the first set of Kotlin Koans exercises, and finishing that off was about a dozen obvious keystrokes (it % 2 == 0) so I probably should have finished that article triumphantly with that.

Instead I look at it this morning and went "oh that was super easy, barely an inconvenience", and finished it off. So much for story pacing.


Numbers in Kotlin

The code for these examples is on GitHub: NumberTest.kt.

I did need to do a bit of research to nail those 11 keystrokes for the last exercise. It was to finish a lambda that was the predicate for an any call to determine there there were any even numbers in a set. I initially wondered - given Kotlin seems to have a method for everything else - whether it might have an isEven method, so looked into that. No, it doesn't. Just use modulo division. But anyway, I landed on the docs page for Numbers, and had a look at that.

As always, my investigation was implemented via tests:

it("demonstrates ints and longs of same 'human value' are not equal") {
    val i :Int = 24
    val l :Long = 24L

    shouldThrow<AssertionError> {
        i.equals(l).shouldBeTrue()
    }
}

Note I don't need the explicit types in the variable declaration there, I felt it made it more clear what I was doing. Note the L suffix on the Long literal.


it("demonstrates Kotest will handle the casting between ints and longs internally") {
    val i :Int = 17
    val l :Long = 17L

    i shouldBe l
}

it("demonstrates Kotest will handle the casting between ints and floats internally") {
    val i :Int = 17
    val f :Float = 17.0F

    i shouldBe f
}

it("demonstrates Kotest will handle the casting between ints and doubles internally") {
    val i :Int = 17
    val d :Double = 17.0

    i shouldBe d
}

Am quite pleased Kotest takes care of the type check so disparate types can be checked in a human sense. Note the difference in literals for floats (F suffix) and Doubles (just a decimal number).


it("demonstrates one can convert an int to a double") {
    val i :Int = 17
    val d :Double = 17.0

    i.toDouble().equals(d).shouldBeTrue()
}

There are conversion methods to/from all numeric types.


it("demonstrates literals can have _ as a separator") {
    val i :Int = 123_456_789

    i shouldBe 123456789
}

it("demonstrates _ is not just a 1000s separator") {
    val d :Double = 3_6_5.2_4_2_5

    d shouldBe 365.2425
}

One thoughtful thing is that for clarity one can put an underscore in any numeric literal to make groupings clear. Not that my latter example is more clear than just 365.2425, but it's demonstrating the point.


it("has binary and hex literals") {
    val b = 0b1111_1111_1111_1111
    var h = 0xff_ff

    b shouldBe h
}

it("demonstrates binary and hex numbers of same 'human value' ARE equal") {
    val b = 0b1111_1111_1111_1111
    val h = 0xff_ff

    b.equals(h).shouldBeTrue()
    b.shouldBeTypeOf<Int>()
    h.shouldBeTypeOf<Int>()
}

Kotlin also has binary and hex literals (but not octal ones. No great loss in my books). Those literals are just ways of defining an Int, they are not their own numeric sub-type.

There's undoubtedly more to look at with numbers, but my attention got shifted when working out how that shouldBeTypeOf<Int> matcher works.


Type-checking in Kotest matchers

The noteworthy ones are:

  • shouldBeTypeOf<type> matches the exact type (subclasses will not match).
  • shouldBeInstanceOf<type> matches within the type-hierarchy (subclasses will match).

Examples:

describe("Tests of type-checking") {
    it("checks exact type with shouldBeTypeOf") {
        val i = 24

        i.shouldBeTypeOf<Int>()
    }

    it("checks subtype with shouldBeInstanceOf") {
        val i = 17

        shouldThrow<AssertionError> {
            i.shouldBeTypeOf<Number>()
        }
        i.shouldBeInstanceOf<Number>()
    }
}

The code for this example is on GitHub: MatcherTest.kt.


Kotest clues

One neat trick one can do in Kotest is to provide a clue for an expectation:

it("can fail with a clue") {
    try {
        withClue("this is the clue") {
            fail("this is a failure message")
        }
    } catch (e :AssertionError) {
        e.message shouldBe """
            this is the clue
            this is a failure message
        """.trimIndent()
    }
}

This is useful both in the code to make it more clear why we're bothering to assert what we are, and it also comes out in the test failure feedback too. This is what I get if I take out the try/catch from that test above, and run it:

C:\Users\camer\.jdks\openjdk-17.0.1\bin\java.exe
Testing started at 16:28 ...

Misc Kotest tests -- emits a failure message is excluded by test filter(s): Excluded by test path filter: 'Misc Kotest tests -- can fail with a clue'

this is the clue
java.lang.AssertionError: this is the clue
this is a failure message
	at system.kotest.KotestTest$1$1$2.invokeSuspend(KotestTest.kt:25)

I realise Jasmine-esque test framework support this, eg:

expect(actual).toBe(expected, "careful now")

So does xUnit-ish frameworks:

asertEquals(expected, actual, "down with this sort of thing")

But I think Kotest's approach is clearer / tidier / less-of-an-afterthought.


One can also use the asClue method to pass an object into a block of expectations, and that object is then reflected as the clue for the failure:

it("can attach asClue to an object to use it for multiple expectations") {
    val colours = listOf("whero","karaka","kōwhai","kākāriki","kikorangi","poropango","papura")

    try {
        colours.asClue {
            withClue("should not contain green") {
                it.none{it == "kākāriki"}.shouldBeTrue()
            }
        }
    } catch (e :AssertionError) {
        e.message shouldBe """
            [whero, karaka, kōwhai, kākāriki, kikorangi, poropango, papura]
            should not contain green
            expected:<true> but was:<false>
        """.trimIndent()
    }
}

Here I have a clue on the object, and also on the specific expecation.

Note the outer it is a reference to the object being used as the clue. The inner it is a reference to current element of iterating over… the outer it… for the none (none iterates over a collection and returns false as soon as the predicate returns true of any element). The it usage is slightly confusing there, but that's down to me being lazy. And so I could write this paragraph. I could have written that block with some clearer parameter names:

colours.asClue {
    colours ->
    withClue("should not contain green") {
        colours.none{colour -> colour == "kākāriki"}.shouldBeTrue()
    }
}

TBH, I don't know that that is any better.

The code for these examples is on GitHub: KotestTest.kt.


Kotest soft assertions

This solves a problem that has caused me grief a lot of times with other testing frameworks. A test demonstrates how this works:

it("can use soft assertions to let multiple assertions fail and report back on all of them") {
    val actual :Int = 15

    try {
        assertSoftly {
            actual.shouldBeTypeOf<Number>()
            withClue("should be 15.0") {
                actual.equals(15.0).shouldBeTrue()
            }
            actual shouldBe 15
            actual shouldBe 16
        }
    } catch (e :MultiAssertionError) {
        assertSoftly(e.message) {
            shouldContain("1) 15 should be of type kotlin.Number")
            shouldContain("2) should be 15.0")
            shouldContain("expected:<true> but was:<false>")
            shouldContain("3) expected:<16> but was:<15>")
        }
    }
}

Here all of those expectations fail. On other testing frameworks I have used they always bail-out as soon as the first expectation fails, and the rest aren't tested. Often this is fine and exactly correct because subsequent expectations might rely on earlier ones passing. But this is not always they case. Obvs one could create one's own custom expectation to check a whole wodge of stuff at once, but it's nicer to have a simple block construct that JFDI.

Another thing to note is the two forms of assertSoftly. The first rendition just takes a lambda with no argument, and the body of the lambda is implemented the same as one usually would in a test. The second variant takes and argument, and that argument is used as the input for all the expectations. I actually dunno how this syntax works, and I have some reading to do. For now: handy to know, and a good reduction of code repetition. And is also encourages the expectations to stay focused, given they must all be applied to the same object.

Be mindful that my test is contived there: it's testing the behaviour of the failure, rather than being a failing test. Normally the test would be more like this:

it("can use soft assertions to let multiple assertions fail and report back on all of them") {
    val actual :Int = 15

    assertSoftly {
        actual.shouldBeTypeOf<Number>()
        withClue("should be 15.0") {
            actual.equals(15.0).shouldBeTrue()
        }
        actual shouldBe 15
        actual shouldBe 16
    }
}

And given that's a failing test now, we get that in the test-run feedback:

io.kotest.assertions.MultiAssertionError: 
The following 3 assertions failed:
1) 15 should be of type kotlin.Number
	at system.kotest.KotestTest$1$2$1.invokeSuspend(KotestTest.kt:78)
2) should be 15.0
expected:<true> but was:<false>
	at system.kotest.KotestTest$1$2$1.invokeSuspend(KotestTest.kt:63)
3) expected:<16> but was:<15>
	at system.kotest.KotestTest$1$2$1.invokeSuspend(KotestTest.kt:66)

Good stuff.

The code for these examples is on GitHub: KotestTest.kt.


Kotest shouldThrowMessage

This is just a quick one. I didn't think it fitted in any of the other sections. I'm catching a lot of exceptions to test how things "fail" in tests, but still have a passing test. Generally I've been expectimng a specofoc exception type to be thrown, but one can also just look for a specific message:

it("should throw an exception with a specific message") {
    class InquisitionException(message: String?) : Exception(message)

    shouldThrowMessage("No-one expects the... InquisitionException") {
        throw InquisitionException("No-one expects the... InquisitionException")
    }
}

Handy.

The code for this example is on GitHub: MatcherTest.kt.


Function literals with receivers

Further up I was bemused by how this code works:

assertSoftly(e.message) {
    shouldContain("1) 15 should be of type kotlin.Number")
    shouldContain("2) should be 15.0")
    shouldContain("expected:<true> but was:<false>")
    shouldContain("3) expected:<16> but was:<15>")
}

Somehow shouldContain is implicitly being called on the e.message argument I am passing to assertSoftly. IE: to my ignorant eyes I am expecting to be seeing something like this:

assertSoftly(e.message) {
    someThingHere shouldContain("1) 15 should be of type kotlin.Number")
    someThingHere shouldContain("2) should be 15.0")
    someThingHere shouldContain("expected:<true> but was:<false>")
    someThingHere shouldContain("3) expected:<16> but was:<15>")
}

Turns out it's not just magic, it's a feature of function literals in Kotlin. Now I only barely grasp what these docs are saying, but I've made enough sense of them to write some code to test it:

Function types with receiver, such as A.(B) -> C, can be instantiated with a special form of function literals – function literals with receiver.

As mentioned above, Kotlin provides the ability to call an instance of a function type with receiver while providing the receiver object.

Inside the body of the function literal, the receiver object passed to a call becomes an implicit this, so that you can access the members of that receiver object without any additional qualifiers, or access the receiver object using a this expression.

Just to start with, here's a no-surprises function literal:

it("is a baseline test using a simple function expression") {
    val add = {op1:Int, op2:Int -> op1 + op2}

    add(17, 24) shouldBe 41
}

It takes two operands, and adds them.

Stealing the example from the docs, we have this alternative:

it("uses function literals with receivers") {
    val add: Int.(Int) -> Int = { other -> this.plus(other) }

    add(17, 24) shouldBe 41
    17.add(24) shouldBe 41
}

This is nice syntax from Kotlin. Not only can one call this function the "normal" way, one can also call that function with the first argument as a receiver (I guess that's what they mean as a receiver?), and it looks like a method on Int class (the literal 17 is still an Int object).

The implementation above doesn't use idiomatic-Kotlin though, in how I have explicitly referenced this (a reference to the first argument). Here's what they mean about "so that you can access the members of that receiver object without […] using a this expression":

it("uses function literals with receivers and implicit this") {
    val add: Int.(Int) -> Int = { other -> plus(other) } // see there's no this here any more

    add(17, 24) shouldBe 41
    17.add(24) shouldBe 41
}

That's all that assertSoftly function expression is doing.

Just so I was clear on this, I created another test:

it("checks the values of the operands in the function implementation") {
    var op1 = 0
    var op2 = 0
    val add: Int.(Int) -> Int = {
        other ->
        op1 = this
        op2 = other
        this.plus(other)
    }

    17.add(24) shouldBe 41

    op1 shouldBe 17
    op2 shouldBe 24
}

This passes, so my hypothesis is right.

My grasp of how the docs explain this is still tenuous. I didn't really see how it worked until I tried it, so I need to think some more about this stuff. Or perhaps better understanding will come from just doing. I have to say that this synxtax doesn't mean much to me: Int.(Int) -> Int = {}, and I can't correlate it back to add(17, 24) and 17.add(24).

I guess looking at the assigment from the test:

val add: Int.(Int) -> Int = { other -> plus(other) }

That's just this:

val variableName: type = value

Where:

  • add is the variableName
  • Int.(Int) -> Int is the type
  • { other -> plus(other) } is the value

The only confusing bit is the type, but I guess if I use different types it might make more sense:

it("clarifies the syntax a bit by using different types") {
    val repeat: String.(Int) -> String = { other -> repeat(other) }

    "Z".repeat(11) shouldBe "ZZZZZZZZZZZ"
}

This is a bit clearer. The type String.(Int) -> String is, I guess, a function that takes a String and an Int, and returns a String. And the String.(Int) syntax means that it can be called using the arg1.function(arg2) syntax.

I think.

The code for these examples is on GitHub: FunctionSyntaxTest.kt.


Anyway, I've been at this a while now: the amount of writing I do (blog and code) only represents about 1/3 of the time I spend reading stuff. So I've been at this most of the day now. Time for a beer. And maybe dinner.

Righto.

--
Adam


PS: I made zero new progress on the koans today, but I learned a lot I reckon.

Friday 9 September 2022

Kotlin: another Friday afternoon, another round of random investigation

G'day:

Because no-one has specifically screams "FFS stop it, Cameron", I'm gonna continue with another random Kotlin noobie investigation / brain dump. Previous similar articles here.


The Nothing and Unit types

Nothing

This came up in the next koans exercise. I read the docs, and looked at an answer on Stack Overflow ("Kotlin - Void vs. Unit vs. Nothing). The key bit seems to be the word cannot in this sentence: "If a function has return type Nothing, then it cannot return normally. It either has to throw an exception, or enter an infinite loop". And the next sentence is an interesting one: "Code that follows a call to a function with return type Nothing will be marked as unreachable by the Kotlin compiler". I wrote a test (obviously ;-)):

class NothingTest : DescribeSpec( {
    describe("Tests of the Nothing type") {
        val EOL = System.lineSeparator()

        val output = SystemLambda.tapSystemOut {
            try {
                outputUntilDie(3)
            } catch (e: Exception) {
                println(e.message)
            }
        }
        output.trim() shouldBe "Iteration 1${EOL}Iteration 2${EOL}Iteration 3${EOL}It died"
    }
})

fun outputUntilDie(max :Int) :Nothing {
    var current = 0
    while (true) {
        current++
        println("Iteration $current")
        if (current >= max) {
            throw Exception("It died")
        }
    }
}

The code below is on Github @ NothingTest.kt

Note in outputUntilDie there is never a normal return condition. The loop either goes forever (assuming I could somehow pass it a max of infinity), or the code bombs out. So whatever comes after the call to outputUntilDie can never be run. If I try to add some, IntelliJ lets me know:

And the compiler gives a warning:

> Task :compileTestKotlin
w: C:\src\kotlin\scratch\src\test\kotlin\language\types\NothingTest.kt: (15, 17): Unreachable code

If I was to change the return type to be Unit (returns no value, as opposed to returns nothing), then I don't get the visual cue or warning.

So I guess it's handy in that regard.

OK, the koans exercise demonstrates a more realistic situation where the code being considered unreachable is important.

import java.lang.IllegalArgumentException

fun failWithWrongAge(age: Int?) {
    throw IllegalArgumentException("Wrong age: $age")
}

fun checkAge(age: Int?) {
    if (age == null || age !in 0..150) failWithWrongAge(age)
    println("Congrats! Next year you'll be ${age + 1}.")
}

fun main() {
    checkAge(10)
}

Here the code won't even compile, because it's possible to get to ${age + 1} with age being null:

C:\src\kotlin\Kotlin Koans\Introduction\Nothing type\src\Task.kt:9:50
Kotlin: Operator call corresponds to a dot-qualified call 'age.plus(1)' which is not allowed on a nullable receiver 'age'.

Telling it that failWithWrongAge has a return type of Nothing mitigates this as processing could have halted already by then.

Unit

And the Unit type is a type that is equivalent to void, it's just a more OO way of doing things, cos it is actually a type, and there is a Unit object. This means everything returns an object, and there's no "special" handling like in Java with void. It's also the default return type in Kotlin according to the docs, so one doesn't need to specify it.


Lambdas

Here I've just messed around with some common higher-order functions available on collections. The code below is on Github @ FunctionsTest.kt

it("is a basic PoC test") {
    val strings = listOf("whero", "kākāriki", "kikorangi")
    val lengths = strings.map {it.length}

    lengths.joinToString(",") shouldBe "5,8,9"
}

map just takes a single param that is the mapping function, so no need for parentheses. And because the callback only receives the one argument, one can just use the default it name. So {it.length} is a lambda that returns the length property value of its it argument (ie: String.length).


class Number(var value: Int, var en: String, var mi: String) {}

it("chains some together") {
    val jumbledNumbers = listOf(
        Number(2, "two", "rua"),
        Number(4, "four", "wha"),
        Number(3, "three", "toru"),
        Number(1, "one", "tahi")
    )
    val maoriNumbers = jumbledNumbers.sortedBy{it.value}.map{it.mi}

    maoriNumbers.joinToString(",") shouldBe "tahi,rua,toru,wha"
}

More of the same except I'm chaining some methods together here.


it("shows using _ to ignore a param in a lambda") {
    val numbers = mapOf(
        Pair("one", "tahi"),
        Pair("two", "rua"),
        Pair("three", "toru"),
        Pair("four", "wha")
    )
    val output = SystemLambda.tapSystemOut {
        numbers.forEach{(_, maori) -> print("$maori")}
    }

    output.trim() shouldBe "tahiruatoruwha"
}

The docs made a point of mentioning this, so I might as well. It's right in the Kotlin idiom to use _ as a parameter name when your lambda receives some arguments it doesn't need. Here the key is the first parameter (so the English version of the number), and I don't need that so this is made explicit by just using _. It's only the second argument I want: the Māori version


class Number(var value: Int, var en: String, var mi: String) {}

it("uses an anonymous function") {
    val jumbledNumbers = listOf(
        Number(2, "two", "rua"),
        Number(4, "four", "wha"),
        Number(3, "three", "toru"),
        Number(1, "one", "tahi")
    )

    val maoriNumbers = jumbledNumbers.sortedWith(fun(e1, e2) = e1.value - e2.value).map{it.mi}

    maoriNumbers.joinToString(",") shouldBe "tahi,rua,toru,wha"
}

For the hell of it here I use the anonymous function syntax (so with the fun keyword) for sortedWith comparator lambda. As the comparator method is just one expression, I don't need the parentheses or the return statement.


it("is an example of a fold") {
    val rgb = listOf("whero", "kākāriki", "kikorangi")

    val colours = rgb.fold("") {
        colours, colour -> if (colours.isEmpty()) colour else "$colours,$colour"
    }

    colours shouldBe "whero,kākāriki,kikorangi"
}

And with the block syntax I had no idea how to define my own params (instead of just the situation where there's only one, and the default it would do), but it's pretty straight forward as it turns out. The bit I didn't guess right was the parameter definitions are inside the block, not before it like I kinda wanted to do.

I also found out that there is no ternary operator (? :) in Kotlin. if is an expression, so it does the same thing. Slightly more verbose, but it's OK. I don't like using else in my code though (but I don't might the "else" operand when using ? :. Hrm…?


it("is an example of a reduce") {
    val redWhiteAndBlue = listOf("whero", "mā", "kikorangi")

    val colours = redWhiteAndBlue.reduce {colours, colour -> "$colours,$colour"}

    colours shouldBe "whero,mā,kikorangi"
}

This shows ths difference between fold and reduce:

  • fold takes a specific initial value as a first argument.
  • reduce uses the first element of the collection as the initial value.

groupBy higher-order function

Ooh I just spotted this in the docs. Kotlin has a gropupBy higher-order function. It does what it says on the tin. here's a contrived example:

it("groups the words by their first character, capitalising each word in the result") {
    val words = listOf(
        "tahi","rua","toru","wha","rima","ono","whitu","waru","iwa","tekau", // 1-10
        "whero","karaka","kōwhai","kākāriki","kikorangi","poropango","papura", // ROYGBIV
        "rāhina","rātū","rāapa","rāpare","rāmere","rāhoroi","rātapu" // Mon-Sun
    ).sortedWith(Collator.getInstance(Locale("mi_NZ")))

    val groupedByLetter = words.groupBy(
        { word -> word.first()},
        {word ->  word.replaceFirstChar(Char::titlecase)}
    ).toSortedMap()

    groupedByLetter shouldBe mapOf(
        Pair('i', listOf("Iwa")),
        Pair('k', listOf("Kākāriki", "Karaka", "Kikorangi", "Kōwhai")),
        Pair('o', listOf("Ono")),
        Pair('p', listOf("Papura", "Poropango")),
        Pair('r', listOf("Rāapa", "Rāhina", "Rāhoroi", "Rāmere", "Rāpare", "Rātapu", "Rātū", "Rima", "Rua")),
        Pair('t', listOf("Tahi", "Tekau", "Toru")),
        Pair('w', listOf("Waru", "Wha", "Whero", "Whitu"))
    )
}

I like that.


Primary and secondary constructors

You saw I had that wee Number class in my tests:

 class Number(var value: Int, var en: String, var mi: String) {}

Initially I had written that long-form like this:

class Number {
    var value = 0
    var en = ""
    var mi = ""

    constructor(value: Int, en: String, mi: String) {
        this.value = value
        this.en = en
        this.mi = mi
    }
}

But IntelliJ admonished me with this:

And I went "um… primary and secondary constructors are a thing, are they? OK. Go for it pal". And it squizhed everything up for me.

I RTFMed and right there on the class page it says this:

A class in Kotlin can have a primary constructor and one or more secondary constructors. The primary constructor is a part of the class header, and it goes after the class name and optional type parameters.

[…]

The primary constructor cannot contain any code. …

Ah OK this makes sense if one just wants simple class with some property values (like I do here), there's no need for so much boiler plate to achieve this. Nice. I'm gonna need to read the rest of that section about initializer blocks and the like. But: tomorrow. I've messed around enough for one afternoon.


That was quite short, but it reflects the forward progress I made in a few hours exploration. The good thing is: the more I find out about Kotlin, the more I want to find out about it.

Righto.

--
Adam

Sunday 4 September 2022

Kotlin: there's no such thing as static, apparently

G'day:

Whilst writing today's previous article ("CFML: invokeImplicitAccessor on CFCs"), I noted that CFML does not support implicit accessor methods on static properties.

I wanted to look at how another language handles the same, and the only languages I personally know anything about that has accessors defined on the properties themselves are C# and Kotlin (that's not to say there aren't others, undoubtedly it's very prevalent; those're the two I know about). My C# is even worse than my Kotlin (if you can believe that), so I decided to check Kotlin. Plus, like, I need to know about Kotlin anyhow, so makes sense.

I was quite surprised to see that Kotlin simply doesn't support the concept of static properties. I mean like I initially went "huh? What? Why?", but figured there was a good reason. I found how Kotlin approximates the equivalent fairly quickly, but it too me longer to find out why.

During all this I tested how property accessors work in Kotlin anyhow, and here's some code for standard property accessors:

describe("property accessor tests") {
    class Person {
        var firstName = ""
            set(value) {
                field = value.replaceFirstChar(Char::titlecase)
            }
        var lastName = ""
            set(value) {
                field = value.replaceFirstChar(Char::titlecase)
            }

        val fullName
            get() = "$firstName $lastName"
    }

    it("uses getters/setters") {
        var person = Person()
        person.firstName = "jane"
        person.lastName = "roe"

        person.fullName shouldBe "Jane Roe"

    }
}

That's pretty self-explanatory, and no surprises there I think. I guess I was thrown by needing to use field rather than the name of the property. It's "explained" in Properties › Getters and setters › Backing fields, but only as a statement of fact; not why it is the way it is. I did note though, that if I had this:

var firstName = ""
    set(value) {
        firstName = value.replaceFirstChar(Char::titlecase)
    }

Then I get a stack overflow. I am guessing that assignment to firstName itself calls its setter, so we get a bit of a recursive meltdown there. I should test that, I think.

OK, so that's normal properties. What about approximating static ones? Kotlin has this concept of a "Companion objects" which one can define within a class, and - as the docs say - "Members of the companion object can be called simply by using the class name as the qualifier". I'm not gonna re-quote someone else's docs too comprehensively, so just go read them if you like.

The "static" equivalent tests here would be this lot:

class PropertiesTest : DescribeSpec ({

    // ...

    describe("static property accessor tests") {
        it("uses getters/setters") {
            StaticPerson.firstName = "connor"
            StaticPerson.lastName = "macLeod"

            StaticPerson.fullName shouldBe "Connor MacLeod"
        }
    }
})

class StaticPerson {
    companion object OnlyOne {
        var firstName = ""
            set(value) {
                field = value.replaceFirstChar(Char::titlecase)
            }
        var lastName = ""
            set(value) {
                field = value.replaceFirstChar(Char::titlecase)
            }

        val fullName
            get() = "$firstName $lastName"
    }
}

Another observation to make here is that I have not declared StaticPerson as a nested class here, like I did with the "property accessor tests", above. I tried that, but got an error: Modifier 'companion' is not applicable inside 'local class'. I could not find out why this is the case, but: so be it.

I found a good explanation of why Kotlin doesn't implement the concept of static: Why is there no static keyword in Kotlin?. It's worth reading a bunch of the answers. One of the answers refers back to the docs (Object expressions and declarations › Object declarations › Companion objects):

Note that even though the members of companion objects look like static members in other languages, at runtime those are still instance members of real objects, and can, for example, implement interfaces[…]

From that and the other reading, Kotlin does it this way as it makes things more OO than Java's approach. Seems legit, I guess.


I just tested that theory I had about property assignments within the class itself also call the property's setter, and this proves to be correct:

describe("property accessor tests") {
    class Person {
        var firstName = ""
            set(value) {
                field = value.replaceFirstChar(Char::titlecase)
            }
        var lastName = ""
            set(value) {
                field = value.replaceFirstChar(Char::titlecase)
            }

        val fullName
            get() = "$firstName $lastName"

        fun setBoth(firstName:String, lastName:String) {
            this.firstName = firstName
            this.lastName = lastName
        }
    }

    // ...
    
    it("uses setters on internal property assignments") {
        var person = Person()
        person.setBoth("sojourner", "truth")

        person.fullName shouldBe "Sojourner Truth"
    }
}

If it wasn't calling the setters on firstName and lastName, then fullName would not be capitalised.

That's enough learning for a Sunday.

Righto.

--
Adam

Saturday 3 September 2022

Kotlin: the next morning learning Kotlin stuff

G'day:

Whether you like it or not, I'm just gonna continue on from yesterday's random effort: Kotlin: an afternoon learning Kotlin stuff. Hey: no-one's making you read this stuff.


Next koans exercise: trimMargin

TBH I've never needed functionality like this - it seems very edge-case-y? - and am quite perplexed as to why this is put forward so early in the koans exercises. But… well here it is:

describe("trimMargin tests") {
    it("does what it says on the tin") {
        val testString = """
            | has | as a margin character
            | | is the default btw
        """.trimMargin()

        testString shouldBe " has | as a margin character\n | is the default btw"
    }

    it("handles a different margin") {
        val testString = """
            # has "# "
            # as a margin value
        """.trimMargin("# ")

        testString shouldBe "has \"# \"\nas a margin value"
    }
}

Note in the first one it only strips the indentation and the |, not the space after the |. There's no reason why it should, but I did wonder when writing the test.

I guess "better to have, and not need; than to need, and not have". But I hope if Kotlin has taken to the time to add this, then they've also taken the time to pretty much add every other bloody thing one might want to do to a string first. I can only suppose it's quite closely-related to trimIndent (see y/day's article for that one), so kinda makes sense (ish) to have this too. Can you tell I remain unconvinced? OK: moving on.


String interpolation / template expressions in strings

The koans exercise relating to trimMargin also touched on template expressions:

describe("template expression tests") {
    it("resolves a variable in a string literal") {
        val name = "Zachary"

        "G'day $name" shouldBe "G'day Zachary"
    }

    it("resolves a variable in a raw string") {
        val name = "Joe"

        """
            G'day $name
        """.trimIndent() shouldBe "G'day Joe"
    }

    it("can use curly braces to disambiguate where the variable name ends") {
        val prefix = "SOME_PREFIX_"
        "${prefix}REST_OF_STRING" shouldBe "SOME_PREFIX_REST_OF_STRING"
    }

    it("can take more complicated expressions") {
        val name = "Zachary"

        "G'day ${name.uppercase()}" shouldBe "G'day ZACHARY"
    }

    it("can take blocks of code provided they resolve to a string?") {
        val name = "Joe"
        val case = "lower"

        "G'day ${if (case == "upper"){name.uppercase()} else {name.lowercase()}}" shouldBe "G'day joe"
    }
}

Now I would never advocate doing what I have done in that last example; I was just checking to see if it would work.


Handling exceptions in Kotest tests

Whilst writing that "can use curly braces to disambiguate where the variable name ends" test above, I was not sure if using a "wrong" variable name in the template expression might be a runtime thing that I could catch in test. As it turns out it's checked at compile time so I could not test for it, buit it got me looking into how to expect exceptions in a test.

Unsurprisingly Kotest has good support for this (see Assertions › Exceptions).

package system.kotest

import io.kotest.assertions.throwables.shouldNotThrow
import io.kotest.assertions.throwables.shouldNotThrowAny
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.assertions.throwables.shouldThrowAny
import io.kotest.core.spec.style.DescribeSpec
import java.lang.AssertionError

class MatcherTest : DescribeSpec({

    describe("tests of shouldThrow~ variants") {

        class MySpecificException(message: String) : Exception(message)
        class MyDifferentException(message: String) : Exception(message)

        it("expects an exception") {
            shouldThrowAny {
                throw Exception("any old exception")
            }
        }

        it("expects a specific exception") {
            shouldThrow<MySpecificException> {
                throw MySpecificException("My specific exception")
            }
        }

        it("enforces a specific exception") {
            shouldThrow<AssertionError> {
                shouldThrow<MySpecificException> {
                    throw Exception("My specific exception")
                }
            }
        }

        it("expects an exception to not be thrown") {
            shouldNotThrowAny {
                // NOP
            }
        }

        it("expects a specific exception to not be thrown") {
            shouldNotThrow<MySpecificException> {
                // NOP
            }
        }

        it("expects a specific exception to not be thrown, but bubbles up any different exception") {
            shouldThrow<MyDifferentException> {
                shouldNotThrow<MySpecificException> {
                    throw MyDifferentException("My different exception")
                }
            }
        }

        it("deals with test failures when using the special handling of shouldNotThrow<Any> above") {
            shouldThrow<AssertionError> {
                shouldNotThrow<MySpecificException> {
                    throw MySpecificException("This specific exception is NOT expected, so should cause an AssertionError (which the test expects, so still passes")
                }
            }
        }
    }
})

One thing to note here is how I am using this strategy to test "failing" behaviour in assertion:

it("enforces a specific exception") {
    shouldThrow<AssertionError> {
        // code that SHOULD cause an assertion failure here
    }
}

So in this test:

it("enforces a specific exception") {
    shouldThrow<AssertionError> {
        shouldThrow<MySpecificException> {
            throw Exception("My specific exception")
        }
    }
}

I am testing that shouldThrow<MySpecificException> isn't fulfilled - the code the assertion is testing (throw Exception("My specific exception") doesn't throw a MySpecificException, even though that's what the assertion needs). So a "working" test here is that the test code does actually throw an AssertionError. Hopefully that makes sense. It looks confusing because I'm using the testing framework to test its own assertion behaviour. I'm not sure that paragraph make things clearer or just even worse. Sorry.

Anyhoo, everything works exactly how I'd expect.

Defining a class anywhere

You might have noticed this in the test code above:

class MatcherTest : DescribeSpec({

    describe("tests of shouldThrow~ variants") {

        class MySpecificException(message: String) : Exception(message)
        class MyDifferentException(message: String) : Exception(message)

        // ...

        it("expects a specific exception") {
            shouldThrow<MySpecificException> {
                throw MySpecificException("My specific exception")
            }
        }

        // ...

        it("expects a specific exception to not be thrown, but bubbles up any different exception") {
            shouldThrow<MyDifferentException> {
                shouldNotThrow<MySpecificException> {
                    throw MyDifferentException("My different exception")
                }
            }
        }

        // ...
    }
})

I'm able to define classes - seemingly - anywhere I like (see Nested and inner classes). I need a coupla specifically-typed Exception objects in my tests, so I can just define them where I need them. Before I had a second test needing that MyDifferentException instance, I had that class declaration within the it callback for the test itself. I like this.

Null safety & safe calls

This stuff is compile-time safety so I can't actually write tests for it, but I'll just repeat the koan exercise here (not sure this is a copyright violation? Maybe if I just say "fair use!!" I'll be fine ;-))

The want me to convert this (Java code):

public void sendMessageToClient(
    @Nullable Client client,
    @Nullable String message,
    @NotNull Mailer mailer
) {
    if (client == null || message == null) return;

    PersonalInfo personalInfo = client.getPersonalInfo();
    if (personalInfo == null) return;

    String email = personalInfo.getEmail();
    if (email == null) return;

    mailer.sendMessage(email, message);
}

To Kotlin code that fits into this function declaration:

fun sendMessageToClient(
        client: Client?, message: String?, mailer: Mailer
) {
    TODO()
}

class Client(val personalInfo: PersonalInfo?)
class PersonalInfo(val email: String?)
interface Mailer {
    fun sendMessage(email: String, message: String)
}

And my code to leverage the safe call operator (?.) so that it only needs the one if statement. This is reasonably familiar from CFML territory (and I notice PHP8 now has the null-safe opeator as well (?-> (yes, really)), so hopefully I don't mess-up this exercise…

Before we get to how I did mess up this exercise, I'll note this cool thing IntelliJ does. I copy and pasted the body of the Java method over the TODO() place holder, and IntelliJ came up with this:

This kinda felt like cheating, but I wanted to see what it did, and it came up with this:

fun sendMessageToClient(
        client: Client?, message: String?, mailer: Mailer
) {
    if (client == null || message == null) return

    val personalInfo: PersonalInfo = client.getPersonalInfo() ?: return

    val email = personalInfo.email ?: return

    mailer.sendMessage(email, message)
}

I felt relieved about not cheating cos this doesn't solve the "only one if statement" constraint to my liking. I mean it's only got one if, sure, but it's also got two elvis operator bail-outs, and I didn't want those: that's not in-keeping with the intent of the exercise.

More importantly, IntelliJ was as bemused by the getPersonalInfo call as I was. Where's the getPersonalInfo coming from? They provide the implementation of Client, and it's just this:

class Client(val personalInfo: PersonalInfo?)

I kinda guessed that there was some way I was unaware of to enable synthesised accessor methods on the class properties so a getPersonalInfo was available, but try as I might I could not find how to do it, so I gave up and cheated. The answer they were after was this:

fun sendMessageToClient(
        client: Client?, message: String?, mailer: Mailer
) {
    val email = client?.personalInfo?.email
    if (email != null && message != null) {
        mailer.sendMessage(email, message)
    }
}

Yeah all right. I was being too literal. There was not a way of being able to use getPersonalInfo to access personalInfo, they just expected me to access the thing directly. Looking at it now, IntelliJ's Java -> Kotlin conversion even did this for this statement:

// Java
String email = personalInfo.getEmail();

// converted to Kotlin
val email = personalInfo.email

Sigh. But I wonder why IntelliJ managed to automatically convert that one, but not the getPersonalInfo one? Interesting.

Anyway, I feel dumb now. I mean dumber than before. Grrr.

BTW I did try to just implement a getPersonalInfo method in the Client class just so I could get my example moving, and got this interesting error:

Reading that made me think "OK it's telling me there already is a getPersonalInfo method. OK so WhyTF doesn't it work then?" After cheating and whilst writing these coupla paragraphs up, I did a google and found this on Stack Overflow, in answer to "How to overcome "same JVM signature" error when implementing a Java interface?"):

You could use @JvmField for instructs the compiler not generate getter/setter…

I'm guessing the story is that under the hood Kotlin has generated explicit getters for me for my class's properties, so there's no need (or want) for me to make explicit ones. And Kotlin uses those to enable direct access to the property via client?.personalInfo. Clearly more reading needed on my part here.

I did come across the bit in the Kotlin docs about being implement implicit accessor methods for properties (Getters and setters). I'm sure this is tied into that error message I was getting too. I'm gonna have a look at that stuff… next time. Yes, sorry: there will be a "next time". Probably tomorrow.

OK. I'm gonna treat this dumb feeling I have with beer. Because that'll work.

Righto.

--
Adam