kohesive / injekt

Dependency Injection for Kotlin
MIT License
235 stars 20 forks source link

Inject function type #35

Closed skrugly closed 8 years ago

skrugly commented 8 years ago

how to achive something like this: val function: (Int) -> Int = Injekt.get()

apatrida commented 8 years ago

This is difficult because of not having great ways to differentiate the internal types without looking at implementation details (unsafe?). Maybe with type definitions coming in the future if available to reflection it could be a way to do this safely. I'll see if I come up with something in the meantime.

you CAN do this now as:

class TestGuthub35 {

    @Test fun testInjectFunctionType() {
        Injekt.addSingletonFactory {
            val function: (Int) -> Int = { value -> value + 1 }
            function
        }

        val function: (Int) -> Int = Injekt.get()
        assertEquals(3, function(2))
    }
}

but it isn't a great way to know which function is injected because it is only the types that differentiate it and not much else. If you had a lot of injected functions this could become dangerous.

see below for a wrapper alternative...

apatrida commented 8 years ago

The type above is compiled to:

kotlin.jvm.functions.Function1<? super java.lang.Integer, ? extends java.lang.Integer>" 

which as Type is a ParameterizedType

The factory held in memory is:

Function0<kotlin.jvm.functions.Function1<? super java.lang.Integer, ? extends java.lang.Integer>>

So all of that is dangerous to Injekt since we cannot differentiate really between similar functions which could be in abundance in an application.

In the future Type Alises are coming to Kotlin which are described as:

Type aliases will allow to assign a short name to a type (for example, a function type, or a generic type with a long signature): typealias MouseEventHandler = (MouseEvent) -> Unit

would be useful if that info available at reflection time. it isn't clear now. Another alternative to this is the one in the next comment...

apatrida commented 8 years ago

So you can use the way mentioned above in the first comment, or you can make it safer by wrapping the function with a class that you can specify when injecting, and if you add the invoke function to that class you can make it feel very natural:

    class Int1Action(val function: (Int) -> Int) {
        operator fun invoke(i: Int): Int = function(i)
    }

    @Test fun testInjectFunctionWrapper() {
        Injekt.addSingletonFactory {
            val function: (Int) -> Int = { value -> value + 20 }
            Int1Action(function)
        }

        val action: Int1Action = Injekt.get()
        assertEquals(22, action(2))
    }
apatrida commented 8 years ago

Added to Stackoverflow as http://stackoverflow.com/questions/37550923