Kotlin already supports DI with delegation and ext...
# arrow
r
Kotlin already supports DI with delegation and extension functions natively. Not sure why people insist on using DI with a runtime container. An example of DI with delegation and another one with type arg constrains: https://gist.github.com/raulraja/97e2d5bf60e9d96680cf1fddcc90ee67
t
not as easy. Spring gives DI and Configuration and instantiation with sane defaults
Copy code
@Component
class MyService (repo: MyRepository)

@Repository
class MyRepository ()
Is just too easy. you can put configuration in arugments and spring will handle that. Less things on main function. Spring boot is actually very nice to work with. I leave functional stuff for domain logic/rule/safety and consistency/integration.rules
d
I'd say that the alternative, which provides better compile-time guarantees, is "too hard". Hence my contention above that we need a Spring competitor that doesn't rely on so much runtime reflection to do what it needs to do
b
That's actually an interesting point: construction of tagless interpreters (beans) is explicit in Kotlin/Arrow but implicit (with annotation or constructor args) in Spring
d
I saw the light when someone told me that Spring was a dynamic programming language written in annotations. Could not be unseen after that.
b
Oh, so if all sufficiently-complex applications or languages eventually implement LISP, that makes Spring a poor analogue of LISP macros, then?
d
Yes, precisely!
We have better poor LISP macros in the form of
kapt
anyway
t
Spring is developing "Kofu" DSL, one of the things is having compile time DI. with it, you no longer need anotations.
instead of the
@Component
annotation, you do this:
Copy code
val beans = beans {
  bean<UserRepository>()
  bean<UserHandler>()
}
b
That's interesting, but if it's compile-time DI I'm not sure how it's any better than compile-time DI in Kotlin, e.g.
Copy code
fun <F> MonadError<F, Throwable>.runner(
        logger: Logger,
        s3: AmazonS3
): LoggingS3Ops<F> =
        object: LoggingS3Ops<F>, MonadError<F, Throwable> by this {
            override val LOG = logger
            override val s3 = s3
        }
unless dependency injection is still automatic or by reflection?
d
That's what I'm wondering. Seems to me that you'd still have to do the injection part by reflection
Better type safety than annotations but still "magic"
you could do bean(::UserRepository) and have polyadic overloads with reified type params, and then you would need less reflection
And you would still need @Transaction method annotations
Which is why I continue to say Kotlin is "pretty close but not quite there yet"
b
@Transaction
is ... interesting
although there isn't any reason it couldn't be replaced with
Transactable<F>
, I think
then
TransactableService<F>: Transactable<F>, Service<F>
In some respects I think that is a more desirable solution since I can get a little careless about
@Transactional
anyway
if I had to specify the transaction propagation behavior and isolation levels per transaction with
Transactable<F>
every time that might be ok
(even if it was implicit with default parameters)
the API would still have all of the methods available for me to choose from when writing the transaction code in IJ
r
If KEEP-87 is approved then the compiler performs the injection piece automatically but even then the boilerplate is so small now with delegation that I can't justify a di framework to avoid creating a module with sane defaults which is also testable.
b
Agree on KEEP-87 but I'm +/- about what is essentially implicits
Some of the cats stuff that threads implicits through everywhere becomes less readable for me
maybe it's just my lack of familiarity with it
r
The complexity of implicits in Scala comes from the resolution scope and the abuse of implicits for other things that are not parameter injection. All that is getting solved in Scala 3 dotty. In the case of Kotlin with KEEP-87 you can require from the compiler an instance by simply asking it to inject it with
with
. The compiler in addition to give you the instance places it in the
this
scope so you don’t even need to prefix it to access its methods and properties.
In the case of Kotlin the resolution is very simple. You can place provider instances in companions, same package or internal in your own package if you wish to override the defaults.
👍 1
We submitted the proposal last month but have not heard yet from Jetbrains or the compiler team.
⏸️ 1