I am trying to pass a function to a function in Kotlin here is my code.
fun validateValueWithFunc(value: String, parsefun: (CharSequence) -> Boolean, type: String){
if(parsefun(value))
print("Valid ${type}")
else
print("Invalid ${type}")
}
The function I'm passing is from Regex class "containsMatchIn"
val f = Regex.fromLiteral("some regex").containsMatchIn
I know about the :: function reference operator but I don't know how to use it in this situation
In Kotlin 1.0.4, bound callable references (those with expressions on left-hand side) are not available yet, you can only use class name to the left of ::.
This feature is planned for Kotlin 1.1 and will have the following syntax:
val f = Regex.fromLiteral("some regex")::containsMatchIn
Until then, you can express the same using lambda syntax. To do it, you should capture a Regex into a single-argument lambda function:
val regex = Regex.fromLiteral("some regex")
val f = { s: CharSequence -> regex.containsMatchIn(s) } // (CharSequence) -> Boolean
One-line equivalent using with(...) { ... }:
val f = with(Regex.fromLiteral("some regex")) { { s: CharSequence -> containsMatchIn(s) } }
Here, with binds the Regex to receiver for the outer braces and returns the last and the only expression in the outer braces -- that is, the lambda function defined by the inner braces. See also: the idiomatic usage of with.
Related
Let's say I have class:
class Foo {
fun doSomething(param1: Int, param2: String, param3: String)
}
and a data class
data class Params(
val param1: Int,
val param2: String,
val param3: String)
Now I want to use the data class arguments to send them to the function, is there a way to do that? Lets say something similar to:
val myParams = Params(1, "2", "3")
val foo = Foo()
foo.doSomething(myparams)
Or by some sort of transformation or method naming. as:
execute(foo, foo::doSomething, myParams)
I doubt this is possible in Kotlin without some tricks. Possible solutions are reflection API and code generation.
Example using reflection:
fun main() {
val myParams = Params(1, "2", "3")
val foo = Foo()
invokeWithParams(foo::doSomething, myParams)
}
fun <T : Any, R> invokeWithParams(func: KFunction<R>, params: T): R {
val paramValues = func.parameters.map { kparam ->
(params::class as KClass<T>)
.memberProperties
.single { it.name == kparam.name }
.get(params)
}.toTypedArray()
return func.call(*paramValues)
}
It should work for static functions, member functions, and extension functions. It may fail with some rarer cases. You should probably add some error handling, e.g. checks if params match.
It won't work on anything else than JVM as reflection is still very limited on other targets.
Also, I'm not entirely sure about this unsafe cast. I think it can't fail, but I'm not 100% sure about it.
Update:
We can make it a little more funny by converting the function to extension operator invoke:
operator fun <T : Any, R> KFunction<R>.invoke(params: T): R
Then we can use it with any function like this:
(foo::doSomething)(myParams)
I'm not sure if this is a good idea though as it is more confusing than an explicit call to the utility function.
I think an extension lambda requires that you pass in the correct argument, but i seems not to do so in the following example.
open class Base {
open fun f() = 1
}
class Derived : Base() {
override fun f() = 99
}
fun Base.g(): Int { return f()}
fun Base.h(xl: Base.() -> Int): Int { return xl()}
fun main() {
val b: Base = Derived() // Upcast
println(b.g())
println(b.h { f()}) // [1]
}
I understand that Base.h takes a function that takes a Base object as its parameter. But line [1] shows that it accepts f(), which is a function that takes no parameter. I was thinking hard about this and I prefixed it with this.f() and it still worked. Not convinced, I modified the code as follows:
open class Base {
open fun f() = 1
}
class Derived : Base() {
override fun f() = 99
}
fun Base.g(): Int { return f()}
fun Base.h(xl: (Base) -> Int): Int { return xl(Base())}
fun test(i:Int) = 1
fun main() {
val b: Base = Derived() // Upcast
println(b.g())
println(b.h { test(1) })
}
This code works. I've run it to verify. And as you can see, b.h() accepts test(), which takes an Int. And this is contrary to the fact that Base.h() takes a Base.
Could you explain this? Thank you for reading.
Note the curly brackets around the functions that are passed in! They change everything.
In the second code, b.h { test(1) } is not passing the function test to b.h. The syntax to pass test to b.h would be b.h(::test), and that does produce an error as you would expect.
b.h { test(1) } passes a function (a lambda expression) that takes a Base as parameter, ignores that parameter, calls test(1) and returns the result. You are basically passing a function that looks like this to b.h:
fun foo(p: Base) = test(1)
You might be wondering how Kotlin knows about Base when you did not write the word Base in the call at all. Well, it can just look at the declaration of b.h, and see that { test(1) } must take a parameter of Base.
The first code snippet is a bit different, because b.h accepts a Base.() -> Int in that case. Base.() -> Int represents a function whose receiver type is Base, that is, a function func that can be called like someBaseObject.func(). Compare this to a function func that takes a Base object as parameter, which can be called like func(someBaseObject).
Again, { f() } is not passing the function f. It is a lambda expression that does nothing but calls f. In this case though, f itself can be passed to b.h (b.h(Base::f)), because it is a function with a receiver type of Base! You can do someBaseObject.f(), can't you? Passing the lambda is similar to passing an extension function that is declared like this (you're just "wrapping" f in another function):
fun Base.foo() = f()
And since the receiver of the function is Base, you are able to access other functions that has Base as the receiver (such as f) in the lambda. You can also specify the receiver (which is this) explicitly.
In Kotlin, the code snippet val f = println() binds the function println (which is a first class object) to the name f.
How do I do the same with the method map, i.e. store it in f?
The syntax val f = map does not work, neither does val f = arrayOf(1,2,3).map. I have tried various variations of the above, with no success.
Also (and related), why does val f = println not work, making the brackets necessary? I come from the functional programming camp (Scheme), where this would be absolutely normal.
(Disclaimer: absolute Kotlin novice, trying to learn it by myself.)
In Kotlin, the code snippet val f = println() binds the function println (which is a first class object) to the name f.
Not true. This assigns the return value result of calling println(), which is Unit, to the variable f. To get a function reference, use ::. But you must also specify the variable type, because it cannot be inferred when the function has overloads:
val f: (Any)->Unit = ::println
A function with a receiver should be specified using the receiving type, like
val f = Any::toString
// or, to bind a specific instance's toString:
val myList = listOf("Hello", "World")
val myListToString = myList::toString
Since map is a higher-order function, it has a long and complicated signature:
val f: Array<Int>.((Int)->String)->List<String> = Array<Int>::map
Lets assume you have a class:
class Person{
var age :Int?
var name :String?
}
and then you have collection of this class, let's call it people :List.
You can call sort as:
people.sortBy{person -> person.age}
or
people.sortBy{person -> person.name}
Im wondering if its possible to write function definition that would sort by a given field? such as:
fun sortbyField(field:???){
peple.sortBy{field}
}
I have no idea if its possible, if so, how to define "field" parameter.
Thanks!
You can make the function parameter accept a function type with a receiver, like Person.() -> T, and then, inside the bodies of lambdas passed to the function, it will be possible to access a property of the implicit receiver:
fun <T : Comparable<T>> sortUsing(fn: Person.() -> T) {
people.sortBy { it.fn() }
}
Usages:
sortUsing { name }
sortUsing { age }
Alternatively, you can pass a callable reference to the property as a functional argument, instead of a lambda:
people.sortBy(Person::name)
people.sortBy(Person::age)
This works for both functional parameters accepting a single argument, (Person) -> T, and for functional parameters with receiver, Person.() -> T.
You can simply use the field as a parameter if you do not call the function as a lambda expression:
people.sortedBy(Person::name)
Maybe the following is something you were looking for:
fun List<Person>.sortByHeader(header: String) = sortedWith(
when (header) {
"header_name" -> compareBy(nullsFirst(), Person::name)
"header_age" -> compareBy(nullsLast(), Person::age)
"header_time" -> compareBy(Person::time)
else -> compareBy(Person::id)
}
)
I used the following data class instead:
data class Person(val id: Int, val name: String?, val age : Int?, val time: LocalDateTime)
This way you can then call it using your header name, e.g.:
pepple.sortByHeader("header_id").run(::println)
pepple.sortByHeader("header_name").run(::println)
pepple.sortByHeader("header_time").run(::println)
pepple.sortByHeader("header_age").run(::println)
I just added some nullsFirst/nullsLast in case you want to have something like that in place as well.
If that is not what you were after, then using a function with receiver as shown by hotkeys answer might be more appropriate for you. The next alternative is using reflection, but I will omit this one, as that should be used rather as a last resort.
Yet another way to write it
people.sortBy { it.name }
In kotlin, when a lambda expect only one argument, you can simplify the syntax: people.sort { it -> it.name } becomes people.sort { it.name }. You have to use the name it though
Given an overloaded function, with one taking a function as parameter. This parameter-function takes no arguments:
def func(param: () => Unit): Unit = {
param()
}
def func(param: Int): Unit = {
println(param)
}
While calling func with an anonymous function works perfect:
func(() => println("it works"))
Using a plain function fails:
def functionAsParam(): Unit = {
println("it works")
}
func(functionAsParam)
Obviously, Scala evaluates functionAsParam and don't pass the function itself to func. Question: How can I (as a user of a library which provides funcs) pass in a non-anonymous function?
There are several ways to do this. Either you explicitly pass the function as parameter:
scala> func(() => functionAsParam)
it works
scala> func (functionAsParam _ )
it works
(These two cases are slightly different though, in the sense that in the first example, you construct new anonymous function with your other function, and in the second example, you indicate that this function should not yet be evaluated, by adding _)
Or you create a variable which is a function and pass it along:
val fval = () => println("It works")
scala> func(fval)
It works
The error comes from the fact that you defined a method, but your func expects a function. Yeah, there is a problem with overloading in scala (in other areas as well). To fix it you need manually convert your method into a function (it's called eta-expantion):
func(functionAsParam _)
If you turn on -Xprint:typer you'll see that scalac expands you method into a function:
val res4: Unit = func({
(() => functionAsParam())
});