kotlinextension-methodsinfix-notationfirst-class-functionsextension-function

type mismatch when create a kotlin infix notation to nest a function to another


I am trying to create a infix notation as a extension function of (Int) -> Int function which is used to nest a function to another.
For example:

class Entry {
    companion object {
        private fun f(x: Int) = x * 2
        private fun g(x: Int) = x + 2

        private infix fun ((Int) -> Int).nest(inner: (Int) -> Int) = { x: Int -> this(inner(x)) }

        @JvmStatic
        fun main(args: Array<String>) {
            val func = ::f nest ::g
            println(func(10))
        }
    }
}

This code works fine, it created a infix notation nest as a extension function to (Int) -> Int function. It requires another (Int) -> Int function and nest them together.
val func = ::f nest ::g is equals to val fun func(x:Int) = f(g(x))
func(10) is equals to (10 + 2) * 2.

But I encountered a problem when I tried to extend this extension notate function to Number interface (for supporting all kinds of numbers).
For example:

class Entry {
    companion object {
        private fun f(x: Int) = x * 2
        private fun g(x: Int) = x + 2

        private infix fun ((Number) -> Number).nest(inner: (Number) -> Number) = { x: Number -> this(inner(x)) }
        // only the infix fun declaration changed ^

        @JvmStatic
        fun main(args: Array<String>) {
            val func = ::f nest ::g
            println(func(10))
        }
    }
}

The kotlin compiler throws an error.

Kotlin: Unresolved reference. None of the following candidates is applicable because of receiver type mismatch: 
private final infix fun ((Number) -> Number).nest(inner: (Number) -> Number): (Number) -> Number defined in ...(path of class Entry)

I am wondering why Int extends from Number, but (Int) -> Int doesn't match to (Number) -> Number.
If I want to extend this notation function to all functions that ask for a number and returns a number (for example, (Long) -> Long (Float) -> Double etc.), what should I do?


Solution

  • Note that a (Int) -> Int is not a kind of (Number) -> Number. Namely, you can give any Number to a (Number) -> Number, but you can only pass Ints to a (Int) -> Int.

    By your logic, I would be able to nest a (Int) -> Int with a (Double) -> Double, since (Double) -> Double is also a (Number) -> Number (by your logic), but that certainly doesn't make sense, does it? You can't pass a Double to a (Int) -> Int.

    Your nest function could be written more generally with generics:

    infix fun <T, U, V> ((U) -> V).nest(inner: (T) -> U) = { x: T -> this(inner(x)) }
    

    It is possible to nest any (U) -> V with (T) -> U, producing a (T) -> V.