How to Resolve Function Template Generics for Signal/Slot System? - kotlin

I'm trying to develop a simplistic signals/slots system in Kotlin. Here's what I have so far:
open class Signal<T : Function<Unit>>() {
val callbacks = mutableListOf<T>()
open fun addCallback(slot: T) {
callbacks.add(slot)
}
open fun emit(vararg params: Any) {
for(call in callbacks) {
call(*params)
}
}
}
fun test(myarg: Int) = println(myarg)
fun main(args: Array<String>) {
val myevent = Signal<(Int) -> Unit>()
myevent.addCallback(::test)
myevent.emit(2)
}
The idea is one would create an instance of Signal along with a generic template to dictate which parameters are used for the callbacks. Callbacks can then be added to the Signal. Finally, whenever the Signal needs to be... well... "signaled", the emit method is used. This method passes all the parameters to the corresponding callbacks if necessary.
The issue is this code results in the following error:
kotlin\Signal.kt:30:4: error: expression 'call' of type 'T' cannot be invoked as a function. The function 'invoke()' is not found
The line in question is:
call(*params)
Any recommendations on how to handle things from here?

This is because Function is an empty interface (source).
The various function types that actually have invoke operators are all defined one by one here, as Function0, Function1, etc.
I don't think you'll be able to create a Signal implementation that may have callbacks with any number and any type of parameters. Could you perhaps get by with only having callbacks with a single parameter?
open class Signal<T> {
val callbacks = mutableListOf<(T) -> Unit>()
open fun addCallback(slot: (T) -> Unit) {
callbacks.add(slot)
}
open fun emit(param: T) {
for (call in callbacks) {
call(param)
}
}
}
fun test(myarg: Int) = println(myarg)
fun main(args: Array<String>) {
val myevent = Signal<Int>()
myevent.addCallback(::test)
myevent.emit(2)
}
(Note that you could replace both usages of (T) -> Unit here with Function1<T, Unit>.)

Related

Kotlin context receivers cannot resolve members of generic type

In the following code, the call member of Animal cannot be resolved even though Cat is specified as context receiver and it has a member named call.
interface Animal { val call: String }
object Cat : Animal { override val call: String = "Meow" }
object Dog : Animal { override val call: String = "Woof" }
fun <T : Animal> acquireAnimal(animal: T, block: context(T) () -> Unit) {
block(animal)
}
fun main() {
acquireAnimal(Cat) {
call
}
}
When I type this inside the lambda, then the IDE seems to suggest that the type of this is Any?.
If I do the same with a function without a generic context receiver, then it seems to get the type right.
Is this a limitation that is by design or is this a bug?
The fact that you cannot access call was a bug, which was fixed in Kotlin 1.7.20.
A workaround for lower versions is:
sealed interface TypeWrapper<out A> {
object IMPL: TypeWrapper<Nothing>
}
fun <T: Animal> acquireAnimal(animal: T, block: context(T) (TypeWrapper<T>) -> Unit) {
block(animal, TypeWrapper.IMPL)
}
fun main() {
acquireAnimal(Cat) {
val x = call // works!
}
}
However, the fact that this doesn't work is intended. Context receivers do not change the meaning of this. Since you are in a global function, this does not mean anything, and the existence of a context receiver does not change that.
Normally, to access the context receiver itself, you need to do a qualified this by appending the generated label for the context receiver:
context(Foo)
fun foo() {
val x = this#Foo
}
However, your context receiver is a type parameter, so according to the rules here, I don't think a label is generated for the context receiver.

How to define a method only when class generic type satisfies a test?

I am trying to do something like the following:
class Event<TPayload>() {
fun subscribe(handler: (payload: TPayload) -> Unit) { ... }
fun subscribe(handler: () -> Unit) where TPayload : Unit { ... }
}
The intention is that instances of Event<Unit> will have two overloads of subscribe(), but other instances will only have one.
The above code will not compile. I tried using extension methods, but would have to use a different name for the extra method, rather than overloading it.
You can define that second function as an extension function so it only appears for Events who have a type of Unit. It's okay to overload the function name. Define it outside the class:
inline fun Event<Unit>.subscribe(crossinline handler: ()->Unit) =
subscribe { handler() }
Test:
class Event<T> {
private val subscribers = mutableListOf<(T)->Unit>()
fun subscribe(handler: (payload: T) -> Unit) {
subscribers += handler
}
fun send(payload: T) {
for (subscriber in subscribers) subscriber(payload)
}
}
fun main() {
val event = Event<Unit>()
// Using verbose syntax to prove it's the extension function being used
// and not a lambda with implicit 'it':
event.subscribe(fun() { println("got unit") })
event.send(Unit)
}
If you use a lambda, the compiler will use the first subscribe function with an implicit it parameter since it takes precedence in overload resolution. But runtime behavior would be the same either way if you aren't using the parameter.

How to use IO compositions in arrow-kt within sync context

I have following interfaces:
interface UserRepository {
fun role(codename: String): IO<Option<Role>>
fun accessRights(roleId: Long): IO<List<AccessRight>>
}
Now trying to use it to compose effectfful operations like this:
private fun retrieveRole(roleCodename: String): IO<Option<RoleTo>> =
IO.fx {
val role = userRepository.role(roleCodename).bind()
role.map { r ->
val ar = userRepository.accessRights(r.id).bind()
RoleTo.of(r, ar)
}
}
The code fails to compile on the second bind (call to userRepository.accessRights(r.id).bind() since bind is suspend function. How I can properly compose two operations? I don't get why first bind works but second doesn't and I don't want to make my function suspend or I have to do it anyway?
This is one frequent gotcha. If you have Option<A> or Either<E, A> and you'd like to act on it, your first instinct is to use it on the block:
either.map { !someIO }
The problem is that the left/none option isn't covered. You should act on both sides, and extract out the IO before executing it.
!either.fold({ ioLogError(it) }, { someIo })
For now, as of 0.10, because fold is an inline function you can use ! inside it too. I cannot promise that'll be the case in the future, as this is an unintended behavior of inline that we left for convenience.
I was able to solve issue using traverse and applicative instance of IO:
private fun retrieveRole(roleCodename: String): IO<Option<RoleTo>> =
IO.fx {
val role = userRepository.role(roleCodename).bind()
val accessRights = role.traverse(IO.applicative()) {
userRepository.accessRights(it.id)
}.bind()
role.map2(accessRights) {
(r, ar) -> RoleTo.of(r, ar)
}
}
Thanks for pointing out about the fact that map expects pure functions.

Kotlin - Automatically match overriden function type?

I'm trying to write a function that is essentially a wrapper method around some other functionality, for instance, some logging function.
I've tried several combinations of inline, generic, reified, etc., but nothing seems to work.
My function looks like this:
fun log(note: String, block: () -> Unit): () -> Unit {
print(note)
return block
}
My idea here is to perform some simple operation on the incoming note, and then just return that incoming function to be used as it was originally.
However, I want to do this around overridden functions like so:
override fun onClick(clicked: View) = log("Green Button") {
// here the regular onClick functionality goes
}
Here, I get an error "Return type is () -> Unit, which is not a subtype of overridden". This makes sense enough, as the function signatures do not match.
However, when I do this with other random functions:
fun test() = log("foo") { ... }
fun otherTest(a: String, b: Int) = log("bar") { ... }
I get no errors, and the interpreter somehow seems fine with this. I also tried looking at something like GlobalScope.launch to take that approach, but I couldn't figure it out.
Is what I'm trying to do possible? If not, is there something close?
I think
inline fun log(note: String, block: () -> Unit): Unit {
print(note)
return block()
}
should do what you want. It can be generalized to
inline fun <T> log(note: String, block: () -> T): T {
print(note)
return block()
}
I get no errors, and the interpreter somehow seems fine with this.
Why is that surprising? Those functions just return () -> Unit. If you do e.g.
fun test() = log("foo") { print("bar") }
then calling test() won't print bar; calling test()() will.
Tell me if my understanding is wrong. This is my approach
Extension function:
fun View.onClickWithLog(str: String, l: () -> Unit) {
setOnClickListener { Log.d("LogTag", str); run(l) }
}
Usage (from Activity):
btnTest.onClickWithLog("My Button String"){
Log.d("Actions from Activity", "Content")
finish()
}
and the output is
D/LogTag: My Button String
D/Actions from Activity: Content
which prints your note first, and execute the actions in the lambda expression.
When you use the = operator to assign something to a fun, the expression on the right hand side is supposed to return the return type of that fun
The original fun onClick(clicked:View) : Unit has return type Unit. When you write
override fun onClick(clicked:View) = ... , the ... is what you get when you call onClick(v) so it should be a Unit instead of a View -> Unit (Not even () -> Unit as in your code)
Take a simpler example. Let say you have fun sum(a:Int,b:Int) : Int. When you write override fun sum(a:Int,b:Int) = ... , ... must be an Int instead of a (Int,Int) -> Int since you expect to get an Int immediately when you call sum(a,b). If you somehow got a let say
val someOtherWayToSum : (Int,Int) -> Int = {...}
and want to use it, you can write
override fun sum(a:Int,b:Int) = someOtherWayToSum(a,b)
In your case, you better just do
override fun onClick(clicked:View){
/* some operation (e.g your log)*/
/* the regular onClick functionality */
}
since you are overriding it and implementing its regular functionality right there anyway.

Extend Mockito verify for Kotlin not working (in a "kotlin way")

I want to extend verify to allow checking multiple commands over the same mocked object but it is not working, it compiles but on run it dont run each command over the same.
Just want to avoid writing more things like:
Mockito.verify(mockedView).initViews()
Mockito.verify(mockedView).setImage(user.photoUrl)
and write more like:
Mockito.verify(mockedView){
initViews()
setImage(user.photoUrl)
}
First try:
#Test
fun onCreate_loadLoginInfo() {
val user = MockUser.user()
presenter.onCreate(mockedView, user)
Mockito.myVerify(mockedView) {
initViews()
setImage(user.photoUrl)
setName(user.name)
setEmail(user.email)
}
}
class Mockito {
companion object
}
fun <T> Mockito.Companion.myVerify(obj: T, func: T.() -> Unit) {
org.mockito.Mockito.verify(obj).func()
}
Second try:
#Test
fun onCreate_loadLoginInfo() {
val user = MockUser.user()
presenter.onCreate(mockedView, user)
Mockito.myVerify(mockedView) {
it.initViews()
it.setImage(user.photoUrl)
it.setName(user.name)
it.setEmail(user.email)
}
}
class Mockito {
companion object
}
fun <T> Mockito.Companion.myVerify(obj: T, func: (T) -> Unit) {
val mock = org.mockito.Mockito.verify(obj)
func(mock)
}
But those are not working, all the tests pass even if I dont call the methods in the presenter, How can I do this?
I had the same problems and wrote Facade around Mockito.
My library allow to verify few calls around one mock object:
val list = mock(MutableList::class)
list.add("String 1")
list.add("String 2")
list.size()
verify(list) {
times(2).add(anyString())
times(1).size()
}
Please look to the readme, maybe it can help you
Correct me if I'm wrong. You want to avoid multiple verify() calls in your test.
#Test fun onCreate_loadLoginInfo() {
// ...
verify(mockedView).initViews()
verify(mockedView).setImage(user.photoUrl)
verify(mockedView).setName(user.name)
verify(mockedView).setEmail(user.email)
}
I modified your second approach little bit:
#Test fun onCreate_loadLoginInfo() {
// ...
verifyAll(mockedView) {
it().initViews()
it().setImage(user.photoUrl)
it().setName(user.name)
it().setEmail(user.email)
}
}
fun <T> verifyAll(mock: T, func: (() -> T) -> Unit) {
func { Mockito.verify(mock) }
}
As you can see now we are passing functional argument to func() and need to use it appropriately (use it as function, not as object).
You should do it like that. verify must be called before each mock method invocation.