Saturday, 19 November 2022

Off-topic / no technical content: I have a dilemma

G'day:

This is off-topic and has no technical content to it at all. It is about a personal situation I have. This is the only place I have to post large wodges of text publicly, hence putting it here. And hey it's my blog: I'll post what I want ;-)

I have a dilemma.

Context / history

My neighbour and I share a front door to the street, and we had got onto nodding terms with each other. When her new baby arrived I fussed a bit over the new wee girl and got to talking more. My neighbour has three girls, approximately 15, 8, <1. They live in a flat that is at most 2 bedrooms. It will be like mine, which is to say it will be very very basic.

She is on a key meter for her electricty, and these display the balance on the meter, which is inside the front door, so I pass it daily. Often (once a month? Every coupla months?) it's in the red, and their power is off.

We each pay for one of the lights in the shared area (stairs, hallway to the front door), and I have been making a point of giving her £20 every few months to cover her share of it.

She has had bailiffs around chasing her ex, who owes the council money for parking fines.

She has had the police around to escort her current partner away from the flat, as he was being abusive. He is, btw, back.

Someone from a charity brought a bag of food around "for the lady who lives upstairs with the kids. She looks like she could do with some food".

I think it's reasonable to say she's struggling with life.

Recently she came to me in a flap saying she hd a debt she needed to pay now, and she couldn't and could she borrow £20? I offered £50, and she did not hesitate to take it. I told her she did not have to pay me back as I could afford it, and she needed it more than me. This is not terribly altruistic of me: I give £50 to charity each month, and that month I gave it to her instead. [shrug].

A few weeks later she texted me saying she needed another £20 in her account as she needed to pay a bill urgently. And she'd pay me back. I transferred £50, and reassured her she didn't need to pay me back, but that we can't keep operating like this.

She's knocked on the door to give me £20 back - I reiterated she didn't need to but she insisted - but within the day she meekly came back and asked if she could take it back again. Because reasons. Fine.

This is all fine. Thusfar.

However now she is texting me every week almost for more money, and I have drawn a line under this enterprise saying "look, 'borrowing' money from your neighbour is not a workable way of solving your issues. I don't know what is, but this has to stop. Please don't ask me for more money".

The calls/texts continue. I am ignoring them. She is still friendly when I meet her on the landing or on the street.

She has actually paid back the £120 she ended up "owing" me. I asked he to keep it: she refused.

Here is my dilemma

She's broke. The UK is not a good place to be broke. Social services are struggling, food banks are struggling. Really a lot of people are struggling. I don't want anyone reading this as anything as a fact that is contextually significant: she is from an eastern European country, her English is not great and I suspect her education is not great either. She does not appear to have any family over here.

On the other hand I am not struggling. I can afford to just give her the money (and I can also afford to also keep giving £££ to charity each month too if I wanted to). I'm not a fountain of cash, but £50 here or there doesn't matter.

Theoretically I stand by my position of "asking yer neighbour for money is not a sustainable solution to your problems". But she has kids who need to eat & have a roof & be warm. Should theoretical economics trump hungry kids?

I have found myself thinking "this is not my problem" or "this should not *be* my problem". But it's my neighbour. Someone in my community. Who needs help and has asked for help.

Whether or not the govt or local council should be dealing with this is neither here nor there. We have a shit, evil (actually evil) government, and we know they don't give a shit about poor people. I can't change the govt (other than one vote at a time), but I can change this situation maybe.

I feel I am being more "middle-class" than I am being a caring person. But all the points above nag at me. I'm currently holding the position of "no, no more money; I am not the solution to your problems". But I suspect I might be mistaken, or being overly "priveleged" about this because I can afford to prevaricate over principles vs hunger.

Thoughts? And if you don't wanna put them publicly here, my email address is on the blog's Communications policy page. Or a lot of my readers know other channels to ping me on and discuss.

Righto.

--
Adam

Thursday, 3 November 2022

Kotlin: more operator overloading

G'day:

The Kotlin koans are still focusing on operator overloading, so so am I. Previously (Kotlin: overriding operators), I had a mess around with overloading:

This evening I'm gonna look at a coupla others.

Plus operator

Like… just the + operator. As in a + b.

This is a daft example, but I'm gonna define a buildable Critter class. A Critter can have a number of arms, legs and heads. Why? Oh I don't know. It just seemed funny to me at the time.

To do this I need to overload the plus operator multiple times:

class Critter(){
    private var heads = mutableListOf<Head>()
    private var arms = mutableListOf<Arm>()
    private var legs = mutableListOf<Leg>()

    operator fun plus(head: Head) : Critter {
        heads.add(head)
        return this
    }
    operator fun plus(arm: Arm) : Critter {
        arms.add(arm)
        return this
    }
    operator fun plus(leg: Leg) : Critter  {
        legs.add(leg)
        return this
    }
}

I'm overloading the plus operator three times. One each for Critter + Head, Critter + Arm, Critter + Leg. Note also that I'm specifically returning the Critter itself from these operations, so that further operations can be chained.

For the purposes of this exercise, the Arm, Leg, Head classes are a bit light on implementation:

class Arm
class Leg
class Head

For the test, I've also added a helper method:

fun describe()= """
        Critter with ${heads.size} ${English.plural("head", heads.size)},
        and ${arms.size} ${English.plural("arm", arms.size)},
        and ${legs.size} ${English.plural("leg", legs.size)}
    """.trimIndent().replace("\n", " ")

(That pluralising thing is a third-party library I dug up. No need to worry about that just now).

And now we can have the rest of our test code (all the above was the "arrange" part of the test):

val critter = Critter()
critter + Head()
critter + Arm() + Arm()
critter + Leg() + Leg() + Leg() + Leg()

critter.describe() shouldBe "Critter with 1 head, and 2 arms, and 4 legs"

It's silly, but I quite like that.

For the sake of completeness, I've tested the chaining works with different types:

fun `it can mix-up the body parts` () {
    val critter = Critter() + Head() + Arm() + Leg() + Leg() + Arm() + Leg() + Head() + Leg()

    critter.describe() shouldBe "Critter with 2 heads, and 2 arms, and 4 legs"
}

Invoke operator

I've messed around with this sort of thing with PHP's __invoke before. The invoke operator function represents the () operator one calls functions with; IE: if f is the function, one uses the () operator to call it: f(). Implementing this operator on a class lets one call objects like methods. Should one want to do that. Which, admittedly, is pretty rarely. But still.

class Logger {
    private var _log = mutableListOf<String>()
    val log: List<String>
        get() = _log

    operator fun invoke(message: String) {
        _log += message
    }
}

Here I have a logger class whose objects can be called as methods:

val logMessage = Logger() // logMessage is an object: an instance of Logger
logMessage("This is a log message")
logMessage("This is another log message")

The bit that effects this is the invoke function.

And the assert part of the test:

logMessage.log shouldBe listOf("This is a log message", "This is another log message")

The next thing I thought to try is how I might have a variadic invoke function: one I can pass any number of arguments to. This example is a bit contrived, but it shows it. Here a Task is an object that is initialised with a lambda, and when the task is invoked as a function, it runs the lambda with whatever the invocation is passed:

class Task(var lambda : (args: Array<String>) -> List<String>) {
    operator fun invoke(vararg args: String) : List<String> {
        return lambda(arrayOf(*args))
    }
}

One thing that is annoying is that a lambda can't be defined as having a vararg parameter, so I need to use an array of things there. However the invoke function can use vararg, so that's cool.

The * there is the spread operator, which converts the varargs passed to the invoke call to an array that the lambda needs. I could not find any other docs for this other than a reference in the vararg docs linked in the previous paragraph.

So I create my task with its lambda:

val task = Task { args -> args.toList() }

And I check the results:

val taskResult = task("This", "is", "a", "variadic", "invoke", "handler")
taskResult shouldBe listOf("This", "is", "a", "variadic", "invoke", "handler")

Both those lines look similar, so it's important to note the first one is passing six separate arguments to task (which is an object remember, not a function!), and the second line is one list with six items. The lambda coverted the varargs from the invoke call to the list it returned.


And that is as far as I got tonight, so I'll leave off here.

Here's the code:

Righto.

--
Adam

Monday, 31 October 2022

Kotlin: ranges

G'day:

Yet another example of me starting a Kotlin koans exercise, seeing the first interesting word in the koans task description and getting lost in researching how the thing works.

Test class names

This is just an aside. You know how I've been showing all these test methods like this (spoiler for the next section):

@Test
fun `a range of integers should be an IntRange`() {
    val range = 1..10

    range.shouldBeInstanceOf<IntRange>()
}

Where the method name is in back-ticks and a human-readable sentence? On a whim I went "I wonder if…" and I tried this:

internal class `Tests of ranges` {

    @Nested
    inner class `Tests of ranges on Ints` {
    // etc

And it only bloody works on class names too! How cool is that?

In the test output that lot shows like this:


Ranges

From the docs:

A range defines a closed interval in the mathematical sense: it is defined by its two endpoint values which are both included in the range.

Let's look at some tests that show how they work:

fun `it should return true for a value in a range`() {
    val range = 1..10
    val value = 5

    (value in range) shouldBe true
}
fun `it should return false for a value not in a range`() {
    val range = 1..10
    val value = 11

    (value in range) shouldBe false
}
fun `it should return true for values at the boundaries of the range`() {
    val range = 1..10

    (1 in range) shouldBe true
    (10 in range) shouldBe true
}

Those are pretty straight forward. What type is it?

fun `a range of integers should be an IntRange`() {
    val range = 1..10

    range.shouldBeInstanceOf<IntRange>()
}

And how to use it?

fun `it can be iterated over`() {
    val range = 1..10
    var list = mutableListOf<Int>()

    list = range.fold(list) { acc, i -> acc.apply { add(i) } }

    list shouldBe listOf(1,2,3,4,5,6,7,8,9,10)
}

It can have a step:

fun `it should return true for values at the steps`() {
    val range = 1..10 step 2

    listOf(1,3,5,7,9).forEach {
        (it in range) shouldBe true
    }
}

Also be mindful of what elements are in the range:

@Test
fun `the upper bound might not be included`() {
    val range = 1 until 10 step 2

    (10 in range) shouldBe false
}
fun `it should return false for values not at the steps`() {
    val range = 1..10 step 2

    listOf(2,4,6,8,10).forEach {
        (it in range) shouldBe false
    }
}

Note the type on a stepped-range is different:

fun `a stepped range is an IntProgression`() {
    val range = 1..10 step 2

    range.shouldNotBeInstanceOf<IntRange>()
    range.shouldBeInstanceOf<IntProgression>()
}

They work in reverse:

fun `a step can be downwards`() {
    val range = 10 downTo 1 step 2

    listOf(10,8,6,4,2).forEach {
        (it in range) shouldBe true
    }
    (1 in range) shouldBe false
}

And one can access some properties about the range:

fun `it has some properties`() {
    val range = 1..10 step 2

    range.first shouldBe 1
    range.last shouldBe 9
    range.step shouldBe 2
}

Note how it's the max value here, not the upper bound of the range.

And they can be reversed (and by inference here, compared):

fun `it can be reversed`() {
    val range = 1..10 step 2
    val reversed = range.reversed()

    (reversed == (9 downTo 1 step 2)) shouldBe true
}

One can make a range of doubles, but they're not iterable:

fun `it does not implement iterator like a range of ints does`() {
    val intRange = 1..10
    intRange.shouldBeInstanceOf<Iterable<*>>()

    val doubleRange = 1.0..10.0
    doubleRange.shouldNotBeInstanceOf<Iterable<*>>()
}

However one can still check whether something is within the range:

fun `its elements can be compared to other values`() {
    val range = 1.0..10.0
    val value = 5.5

    (value in range) shouldBe true
}

Ranges of chars are more useful (I guess…):

fun `a range of chars can have a step`() {
    val range = 'z' downTo 'a' step 2

    listOf('z','x','v','t','r','p','n','l','j','h','f','d','b').forEach {
        (it in range) shouldBe true
    }
}

One can have ranges of strings, but they don't seem to support stepping or downto

fun `a range of strings can have a step`() {
    val range = "a".."f"

    listOf("a","b","c","d","e","f").forEach {
        (it in range) shouldBe true
    }
}

But they can be multi-char:

fun `the strings in the range can be multi-character`() {
    val range = "aa".."cc"

    listOf("aa","ab","ac","ba","bb","bc","ca","cb","cc").forEach {
        (it in range) shouldBe true
    }
}

I tried to be clever and create a range of strings and then reverse it, but Kotlin sez no:

I can make a range of enum elements (which I thought was at least a bit clever of me to try):

@Test
fun `a range of MaoriNumbersEnum should not allow other elements from the enum`() {
    val range = MI.TAHI..MI.WHA // tahi, rua, toru, wha

    (MI.RUA in range) shouldBe true
    (MI.RIMA in range) shouldBe false // rima is 5
}

@Test
fun `a range of enums is not iterable` () {
    val range = MI.TAHI..MI.WHA

    range.shouldNotBeInstanceOf<Iterable<*>>()
}

But as you can see, they too aren't iterable.

I think I need to set myself an assignment to write extension functions so I can iterate over my Maori numbers. But not this evening.

Lastly - and so I can answer the koans question - any class that implements Comparable can be used in a range.

Here's my MyDate class back from the previous article:

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    override fun compareTo(other: MyDate): Int {
        return when {
            year != other.year -> year.compareTo(other.year)
            month != other.month -> month.compareTo(other.month)
            else -> dayOfMonth.compareTo(other.dayOfMonth)
        }
    }
}

And using it in a range:

fun `a range of objects implementing Comparable can be used as a range`() {
    val testDates = MyDate(1955, 3, 25)..MyDate(1955, 3, 28)

    testDates.start shouldBe MyDate(1955, 3, 25)
    testDates.endInclusive shouldBe MyDate(1955, 3, 28)

    val restDay = MyDate(1955, 3, 27)
    (restDay in testDates) shouldBe true
}

(26 bonus points for anyone who can work out what that date range is. There is a clue in there. And I doubt anyone who is not an NZ cricket fan has a hope in hell of getting it).

It looks like making an iterable range of MyDate objects isn't that hard (esp if I get Copilot to do a lot of it). First I need to implement a "next" method in MyDate:

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    private val date = LocalDate.of(year, month, dayOfMonth)

    override fun compareTo(other: MyDate): Int {
        return when {
            year != other.year -> year.compareTo(other.year)
            month != other.month -> month.compareTo(other.month)
            else -> dayOfMonth.compareTo(other.dayOfMonth)
        }
    }

    fun nextDay(): MyDate {
        val nextDate = date.plusDays(1)
        return MyDate(nextDate.year, nextDate.monthValue, nextDate.dayOfMonth)
    }
}

Then I need to create the range class:

class MyDateRange(override val start: MyDate, override val endInclusive: MyDate) : ClosedRange<MyDate>, Iterable<MyDate> {
    override fun iterator(): Iterator<MyDate> {
        return object : Iterator<MyDate> {
            var current = start

            override fun hasNext(): Boolean {
                return current <= endInclusive
            }

            override fun next(): MyDate {
                val result = current
                current = current.nextDay()
                return result
            }
        }
    }
}

Copilot wrote all of that for me, once I gave it the class name, and then asked it to implement Iterable as well.

I have to admit I kinda spiked this code to see how hard it would be to implement before deciding whether to even include this in the article, so I back-filled the test on this one.

fun `an iterable range can be made by implementing Comparable and Iterable`() {
    val testDates = MyDateRange(MyDate(1955, 3, 25), MyDate(1955, 3, 28))

    (MyDate(1955, 3, 27) in testDates) shouldBe true
    (MyDate(1955, 3, 29) in testDates) shouldBe false

    var list = mutableListOf<MyDate>()

    list = testDates.fold(list) { acc, date -> acc.apply { add(date) } }

    list shouldBe listOf(
        MyDate(1955, 3, 25),
        MyDate(1955, 3, 26),
        MyDate(1955, 3, 27),
        MyDate(1955, 3, 28)
    )
}

This is a good start for me to work out how to make a fully-functioning enum range. Later.

And that is a chunk of code, and not much text. I think the tests largely speak for themselves; the only tricky bit was that last MyDateRange thing, and I'll come back to that. Lemme know if this strategy of using "tests as narrative" works? Cheers.

The code is here: RangesTest.kt.

Righto.

--
Adam

CFML: AND and OR operators not doing what one might expect

G'day:

A question came up on the CFML Slack forums today about some "unexpected behaviour" with ColdFusion's and operator. Here's an example (CF2021):

cf-cli>5 && 7
7

cf-cli>5 || 7
5

Compared to Lucee:

CFSCRIPT-REPL: 5  && 7
true
CFSCRIPT-REPL: 5 || 7
true

The questioner was puzzled by ColdFusion's behaviour, expecting to see something more like Lucee's result.

The docs for these (Expressions-Developing guide › Boolean operators) says this:

AND or &&
Return True if both arguments are True; return False otherwise. For example, True AND True is True, but True AND False is False.

On the face of it Lucee is getting it right, and ColdFusion is being weird, however this is a case of Lucee following the docs, and the docs being wrong.

CFML's behaviour with the AND and OR operators is not quite as the docs say, they behave the way described clearly in the JavaScript docs (JavaScript › Expressions and operators › Logical AND (&&)):

More generally, the operator returns the value of the first falsy operand encountered when evaluating from left to right, or the value of the last operand if they are all truthy.

I only cite the JS docs cos they're clear and well-worded, not because of any connection to CFML.

I guess it works like this in languages with truthy/falsy values as opposed to strict true/false because the operand-values can have meaning beyond just being boolean.

I checked some other languages I had to hand:

adam@DESKTOP-QV1A45U:~$ node -i
> 5 && 7
7
> 5 || 7
5

(And JS run in the browser is the same)

Ruby:

adam@DESKTOP-QV1A45U:~$ irb
irb(main):001:0> 5 && 7
=> 7
irb(main):002:0> 5 || 7
=> 5
irb(main):003:0>

Groovy:

groovy:000> 5 && 7
===> true
groovy:000> 5 || 7
===> true
groovy:000>

PHP:

php > echo 5 && 7;
1
php > echo 5 || 7;
1

Clojure:

user=> (and 5 7)
7
user=> (or 5 7)
5

There's a strong precedent there, but obviously it could have gone either way. CFML just went the way shown. So it's a bug in Lucee that it doesn't work the same as ColdFusion.

BTW: ColdFusion has worked that way since at least CF5. I just checked.

Why am I writing this? Well cos it's not correctly documented anywhere, so I figured I'd jot something down in the hope Google picks it up.

Righto.

--
Adam

Sunday, 30 October 2022

Kotlin: overriding operators

G'day:

Apologies in advance if this article is a bit rubbish. I'm not really "feeling it" today with the writing, but I need to crack on with the Kotlin stuff so am hoping if I get a move on with something, I'll find my motivation as I go. This has never once worked in my life before, but let's see what happens this time.


Prelude

OK so the next Kotlin koans exercise is about operator overloading, hence looking at that now. My one enduring memory of when I did C++ back in polytech (this was in 1993 or 1994) was my tutor's advice re operator overloading: just because it's possible doesn't mean one should do it: it just makes for messy, hard to understand code. 28 years later (OMG: 28!!) I am using a language that supports operator overloading for the first time. I mean I've messed around with Ruby, Groovy etc which allow it, but I never got far enough with those to need to look at it.

I think this is possibly why I'm unmotivated to write on this: I need to know about it, but I don't really want to know about it, if you see what I mean. Anyway: harden up, Cameron. Get on with it.


Unary plus operator

The first interesting thing I landed on was the notion of a unary-plus operator. WTF? I knew what a unary-minus operator does: switches the sign on the operand. But what would a unary-plus operator do? The operator overloading docs page mentioned it but didn't explain it; and the operators listing in the docs (Keywords and operators › Operators and special symbols) didn't mention it at all (or unary-minus for that matter). I've just found there's a docs page for unary-plus specifically, and it says:

operator fun unaryPlus(): Int

Returns this value.

That was a bit unhelpful until I checked the equivlent page for unary-minus:

operator fun unaryMinus(): Int

Returns the negative of this value.

OK: got it. It literally just returns the value (constrast with the minus one that returns the negative of the value, to slightly belabour the point). This does not seem useful, so I figured I was missing something, so googled a bit. I found a doc somewhere (I can't find it now; I'm writing this a week after I did the initial research) that indicated it was added to either C or C++ for the sake of symmetry with unary-minus. I also found that in other languages it does actually have functionality on non-numeric types. In JavaScript it will return the numeric value of a string, eg:

s = "42"
'42'
+s 
42  

And in Java I found that it does similar. What is the purpose of Java's unary plus operator?:

The unary plus operator performs an automatic conversion to int when the type of its operand is byte, char, or short.

Let's have a look at that:

jshell> char c = 'A'
c ==> 'A'
|  created variable c : char

jshell> +c
$2 ==> 65
|  created scratch variable $2 : int

Fair enough. I can work with this.

I had a look at how the unary-plus operator works on the types that support it in Kotlin, with variations on this approach:

@Nested
@DisplayName("Tests of unary plus operator on Ints")
inner class IntUnaryPlusTest {

    @Test
    fun `it does nothing to integer values`() {
        +1 shouldBe 1
    }

    @Test
    fun `it throws a NPE for null Ints`() {
        val i: Int? = null

        shouldThrow<NullPointerException> {
            +i!!
        }
    }
}

I had a version of those for Ints, Doubles, Floats, Longs, Shorts and Bytes.


Operator overloading

Kotlin doesn't support the unary-plus operator on chars, so here's where my experimentation starts. It's pretty simple:

@Nested
@DisplayName("Tests of overloaded unary plus operator on Chars")
inner class CharUnaryPlusTest {
    operator fun Char?.unaryPlus(): Int? {
        return this?.code
    }

    @Test
    fun `it returns the character code of the char as an int`() {
        +'a' shouldBe 97
    }

    @Test
    fun `it returns null if the Char is null`() {
        val c: Char? = null

        val result = +c

        result.shouldBeNull()
    }

    @Test
    fun `it handles double-byte chars`() {
        +'€' shouldBe 8364
    }

    // NB: emoji will not fit into a Char see https://stackoverflow.com/q/70152643/894061
}

All one does is to create a method of your class or - in this case as I am "extending" a built-in class - an extension function, prefix it with "operator", and the method names for the various operators are listed on the docs page I link to in the heading. That's it.

I also did an implementation for Booleans:

operator fun Boolean?.unaryPlus(): Int {
    return if (this == true) 1 else 0
}

// ...

@Test
fun `it returns zero if the boolean is null`() {
    val b: Boolean? = null

    val result = +b

    result shouldBe 0
}

(I left the null test in as it wasn't so obvious: as an arbitrary decision I decided a null Boolean should behave as if it's falsey for this conversion. Equally it could have just stayed as a null).

I also did Strings:

@Nested
@DisplayName("Tests of overloaded unary plus operator on Strings")
inner class StringUnaryPlusTest {
    operator fun String?.unaryPlus(): Number? {
        return this?.toIntOrNull() ?: this?.toDoubleOrNull()
    }

    @Test
    fun `it converts a string integer to an Int`() {
        val result = +"42"

        result.shouldBeInstanceOf<Int>()
        result shouldBe 42
    }

    @Test
    fun `it handles negative numbers`() {
        +"-42" shouldBe -42
    }

    @Test
    fun `it converts a string double to a Double`() {
        val result = +"3.14"

        result.shouldBeInstanceOf<Double>()
        result shouldBe 3.14
    }

    @Test
    fun `it handles a null string`() {
        val s: String? = null

        val result = +s

        result.shouldBeNull()
    }

    @Test
    fun `it handles a string that is not a number`() {
        val s = "not a number"

        val result = +s

        result.shouldBeNull()
    }
}

Again the handling was an arbitrary one. I decided that if it could convert it to an Int, then an Int it will be. Otherwise a Double. Otherwise null.

I also let Copilot take over after the obvious cases. I typed the test label and let it do the rest. It wrote some more examples for collection types along these lines:

@Test
fun `unary plus is not implemented on Array values, but can be overloaded`() {
    operator fun Array<Int>.unaryPlus(): Int? {
        return this.sum()
    }
    +arrayOf(1, 2, 3) shouldBe 6
}

Indeed after the first one, I just had to write @Test and it got the idea. I won't repeat it all here as it's a bit "samey", but I'll include a link to GitHub at the bottom of the article.


Koans requirement: compareTo

That was a lot of digression away from what the exercise actually wanted, which is to implement date-comparison operations on bespoke date class. The example code is this:

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    /* TODO */
}

And I need to be able to do this:

println(date1 < date2)

All this investigation into overloading unary operators has not helped here. I need to overload an operator that has two operands.

Copilot largely came to the rescue here. I created the test file and @DisplayName("Tests of overloading the compareTo operator on MyDate objects") and it created the test skeletons for me:

@Test
fun `it should returns 0 when the two MyDate objects are equal`() {
    TODO()
}

@Test
fun `it should returns -1 when the first MyDate object is less than the second MyDate object`() {
    TODO()
}

@Test
fun `it should returns 1 when the first MyDate object is greater than the second MyDate object`() {
    TODO()
}

It couldn't do better than that as it had no idea what this "MyDate" thing is that I'm on about. However once I typed data class MyDate, it got the idea and had a good stab at the solution:

data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
    override fun compareTo(other: MyDate): Int {
        return when {
            year != other.year -> year - other.year
            month != other.month -> month - other.month
            else -> dayOfMonth - other.dayOfMonth
        }
    }
}

That would work OK, but it failed its own tests because it returned 5 for the comparison in the "less than" test (2016 - 2011 = 5). I coached it a bit, and we finally agreed on this:

override fun compareTo(other: MyDate): Int {
    return when {
        year != other.year -> year.compareTo(other.year)
        month != other.month -> month.compareTo(other.month)
        else -> dayOfMonth.compareTo(other.dayOfMonth)
    }
}

This way it returns one of -1, 0, 1, which is tidier than the actual difference, and has the added bonus of giving us passing tests.

I need one more test though. The requirement is not to us the compareTo method, it's to use the < operator. I will encourage Copilot to write this for me:

(It's only got the red squiggly line cos the function name isn't closed yet).

Nailed it, Copilot. Thanks.

I slung that into the koans window and checked it: pass. I checked their own implementation, and it was slightly simpler:

override fun compareTo(other: MyDate) = when {
    year != other.year -> year - other.year
    month != other.month -> month - other.month
    else -> dayOfMonth - other.dayOfMonth
}

All that when block is a single expression, so we could have eschewed the braces and the return. Good thinking.


Indexed access operator

I decided to do one last exercise. One can make an object accessible via index notation (eg: myObj[i]) via overloading the get operator.

Here's how we expect it to work:

import WordCollections.MaoriNumbersEnum as MI

// ...

@Test
fun `it should return the correct value for the given index`() {
    val numbers = MaoriNumbers(listOf(MI.TAHI,MI.RUA,MI.TORU,MI.WHA))

    numbers[0] shouldBe MI.TAHI
    numbers[1] shouldBe MI.RUA
    numbers[2] shouldBe MI.TORU
    numbers[3] shouldBe MI.WHA
}

And here's the implementation:

data class MaoriNumbers(val numbers: List<MI>) {
    operator fun get(index: Int): MI {
        return numbers[index]
    }
}

Easy. And this is actually a good use of operator overloading I think.

Also note I am using an enum here to restrict the values that my MaoriNumbers object will support:

enum class MaoriNumbersEnum {
    TAHI, RUA, TORU, WHA, RIMA, ONO, WHITU, WARU, IWA, TEKAU
}

This is a call-back to my earlier article "Kotlin: more koans, more random investigation".


OK: that's enough. I seem to've got into the swing of things with the writing after about 15min. This is by no means my best article ever, but I think it's readable, and in writing everything thing down it's solidified things in my head a bit better. Cool. Do give me a rark-up if this reads like I'm just going through the motions a bit, it'd be good to know what other ppl think.

Oh yeah! The code! It's here: /src/test/kotlin/junit/language/operators.

Righto.

--
Adam

Saturday, 29 October 2022

Monumental cock

G'day:

Because Elon Musk is a monumental cock that no-one at all should be encouraging (other than to fuck off to Mars on one of his rocketships, I guess), I'm gonna do my bit and wind-down my usage of Twitter, in favour of Mastodon. The first manifestation of this is I will not longer be putting a Twitter status up when I release a new blog article. Yeah, I know, this is largely a "shrug" sort of thing for almost everyone (incl. myself), I don't have any illusions to the contrary. But if we all do something small: the accumuled results will be something bigger.

However if you use Twitter as a mechanism to know when I've written something, that'll no longer work. I'm on @adam_cameron on Mastodon. I'll be pinging from there in future.

I'll still ping this article on Twitter, but that's it.

Righto.

--
Adam

CFML: addressing confusion around arrays returned from Java methods and using them with CFML code

G'day:

This has come up often enough that it's probably worth having something clear and googleable around for people to find when this crops up for them.

Context

Sometimes it's useful to call a Java method on a CFML object to benefit from functionality that's in Java that's not in CFML. Being able to call split to split a CFML string on a regular expression pattern is a good example:

string = "abcde"
arrayOfChars = string.split("")
writeDump(arrayOfChars)

Lovely.

Problem

So what's the problem? Often there isn't one. However for people who prefer using CFML's object.method() syntax rather than its function(object) syntax, they might get a surprise:

nextChars = arrayOfChars.map((char) => chr(asc(char) + 1))

This results in:

The map method was not found.
Either there are no methods with the specified method name and argument types
or the map method is overloaded with argument types that ColdFusion cannot decipher reliably.
ColdFusion found 0 methods that match the provided arguments.
If this is a Java object and you verified that the method exists, use the javacast function to reduce ambiguity.

Or Lucee (less info, slightly more helpful as to what the issue is):

No matching Method/Function for [Ljava.lang.String;.map(lucee.runtime.type.Lambda) found

What's the problem? Look at the Lucee error. It's pointing out that arrayOfChars is a [Ljava.lang.String (a Java String[]). So that code is trying to call CFML's map method on a Java String[]. Java String[] doesn't have a map method. one has to recall that when one is using object.method() syntax then the type of the object is what dictates what methods can be called. It's not the same with function(object) when function's implementation can take a "close enough" type and cast it to the type it actually needs.

"Workaround"

It's important to note that this would work no worries:

nextChars = arrayMap(arrayOfChars, (char) => chr(asc(char) + 1))

Solution

However sometimes one might want an actual CFML array (so the object passes type-checks, etc), and this is easy enough to achieve:

arrayOfChars = arrayNew(1).append(arrayOfChars, true)

Here we are creating a CFML array, and then using its append method - which will take a Java array and cast it to a CFML array internally - before appending it to the array it's being called on. Job done. Oh the true argument there just means to append the arrays together, rather than appending the first argument into the last element of the first array. Try it yerself to see the difference: pass it false instead.

Summary / proof

string = "abcde"
arrayOfCharsFromSplit = string.split("")

arrayOfCharsAfterAppend = arrayNew(1).append(arrayOfCharsFromSplit, true)

nextChars = arrayOfCharsAfterAppend.map((char) => chr(asc(char) + 1))

writeDump([
    values = [
        arrayOfCharsFromSplit = arrayOfCharsFromSplit,
        arrayOfCharsAfterAppend = arrayOfCharsAfterAppend,
        nextChars = nextChars
    ], types = [
        arrayOfCharsFromSplit = arrayOfCharsFromSplit.getClass().getName(),
        arrayOfCharsAfterAppend = arrayOfCharsAfterAppend.getClass().getName(),
        nextChars = nextChars.getClass().getName()
    ]
])

And the code is in a trycf.com gist today.

Righto.

--
Adam