Search code examples
lambdakotlinsequencepeano-numbers

Can't create sequence of Ints in Kotlin through succesor function. Says "Type Inference failed"


I'm trying to create a stream/sequence of natural numbers from the number 0 and a successor function S, through the generateSequence function.

Here's what I've got:

package core

fun sequenceOfNumbers(): Sequence<Int> {
    return generateSequence(0){x -> S(x)}
}

class S: (Int) -> Int {
    override operator fun invoke(x: Int) = x + 1
}

fun main(args: Array<String>) {
    println(sequenceOfNumbers().take(10).toList())
}

I am aware this may not seem like the best way to solve this, as there already are increment operators, and that there already is a shortcut for generating the first n natural numbers, but I want S to be a class or at least object for some other reasons later.

When I compile it I get the following messages:

Type inference failed: Cannot infer type parameter T in 

fun <T : Any> generateSequence(
   seed: T?,
   nextFunction: (T) → T?
) : Sequence<T>

None of the following substitutions    
(S?, (S) → S?)    
(Int?, (Int) → Int?)
(Any?, (Any) → Any?)
can be applied to    
(Int, (Int) → S)

and

Too many arguments for public constructor S() defined in core.

Other things I've tried is rewriting S as

class S: Function<Int> {
    operator fun invoke(x: Int) = x + 1
}

or changing the generateSequence function to

fun sequenceOfNumbers(start: Int): Sequence<Int> {
    return generateSequence(seed = start, nextFunction = (x: Int) -> S(x))
}

which didn't work either. Last function got the compile messages "Unexpected type specification" and "Unexpected tokens (use ';' to separate expressions on the same line".

Any way to solve this so that the println function prints out the first 10 natural numbers, and still uses the successor class?


Solution

  • The problem in your code that your actually calling the constructor of S, rather than invoke(). You should change to following to make it work:

    return generateSequence(0){x -> S()(x)}
    

    Of course it will be better, if you store S in local variable and reuse it in sequence generator:

    fun sequenceOfNumbers(): Sequence<Int> {
        val s = S()
        return generateSequence(0){x -> s(x)} //generateSequence(0, s::invoke)
    }
    

    Or even better to make S singleton:

    fun sequenceOfNumbers(): Sequence<Int> {
        return generateSequence(0) { x -> S(x)} //generateSequence(0, S::invoke)
    }
    
    object S: (Int) -> Int {
        override operator fun invoke(x: Int) = x + 1
    }
    

    So in the end your code will look like in your example.