In the function declaration, the compiler has no way of knowing the type of T. When calling it, you can give it context. I believe you could fix this by giving T a bound, if you plan on only using it for numbers
fun <T : Number> foo(x: Int = 1, conv: (Int) -> T) = conv(x)
I would like to give conv the default argument { it } which seems to be impossible in your case as well.
Also, { it } does not need further context right?
In my imagination, default arguments are conceptually evaluated/copied into function when it is called and thus { it } should properly work.
then the compiler could (easily) mark this as an error with the same reason why
val b: Bar = foo(1) { it }
would cause it.
Thanks for you both to suggest
fun <T> foo(x: Int = 1, conv: (Int) -> T = { it as T }) = conv(x)
as a workaround. This looks smelly and IDEA warns due to the unchecked cast and I would rather not get runtime exceptions. Btw, why is reified not needed here? I thought the type was erased.
Have you considered adding an overloaded function? fun foo(x: Int = 1) = x
since all you’re trying to do is to cover a specific case with Int as your T type which doesn’t require a function to be generic
@Stas.Shusha Yeah that is the solution that I currently use. I have several of those functions, so I ended up with quite some code duplication that I would have loved to avoid using inline lambdas.
An idea was proposed there that default parameter value could add some additional constraints to the parameter type, but these constraints would be only honored by the compiler if the actual value for that parameter is missing.
For example in this function declaration: fun <T> foo(x: Int = 1, conv: (Int) -> T = { it })
the default value of conv could add a constraint T >: Int (T should be supertype of Int), but that constraint would be ignored if you specify the value of conv when calling the function.
And if you omit the argument, the constraint could help to report an error about the invalid actual type parameter:
foo<String>(1) // ERROR: String is not supertype of Int
foo(1, { it.toString() }) // returns "1"
foo<Int>(1) // returns 1
foo(1) // returns 1