Raw material http://www.msec.it/blog/ Software development ideas, not refined. Sun, 04 May 2025 19:59:53 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 Playing with image generative AI https://www.msec.it/blog/playing-with-image-generative-ai/ Sun, 04 May 2025 19:09:56 +0000 https://www.msec.it/blog/?p=1134 I wanted to try out gpt-image-1, OpenAI’s new model for image generation. It’s rare that I need to generate images for a project (sometimes for fun with my kids), and while the free ChatGPT plan feels too limited for occasional image work, I don’t need a full monthly subscription either.
I also looked online for simple tools that directly use the gpt-image-1 API and support all its features, but I couldn’t find anything that fit my needs.

When you have to produce multiple images with consistent characters and style, a chat-based UI can feel a bit restrictive. So I decided to build a small tool that talks directly to the OpenAI image-generation API. The interface is a very simple node-based editor that lets you link images together—so you can reliably reuse characters or environments across multiple prompts. I’ve also added inpainting support, even though gpt-image-1 doesn’t always honor an inpainting mask perfectly.

If you’re curious, the source code on the github repo, and you can try it out here: https://msec.it/image-workbench

A few things you’ll need to know before getting started:

  • The tool is still pretty rough around the edges—there are a lot of UI/UX improvements I’d like to add.
  • You should provide your own OpenAI API key enabled for the gpt-image-1 model (click the key icon in the tool to set it, it will be stored locally in your browser’s local storage).
  • Because the tool runs entirely in your browser (it’s serverless), you have to click “New project” first and choose an empty local folder where it will save your files and images.

Fell free to test and use it if you think it can be useful for you. And let me know if you have any feedback. Anyway, consider that this is just a little side-project done in my very very limited free time, so I’m not able to promise you any kind of support of future development.

If you want to reach me out, you can find me on X.

]]>
Duel Times is finally published https://www.msec.it/blog/duel-times-finally-published/ Fri, 13 Sep 2024 18:39:24 +0000 https://www.msec.it/blog/?p=1130 After several months of working on it in my limited free time, I’m excited to announce that my little video game, Duel Times, is finally finished and published!

The game is designed with education in mind—its main goal is to help kids sharpen their knowledge of multiplication tables. While there’s no direct competition, players can compare their results with friends, making it a fun and motivating way to complete the game.

You can find all the details about the game on this page. Feel free to share it with anyone—it’s completely free, ad-free, and the source code is open to all.

One of the most enjoyable parts of this project, for me, was experimenting with generative AI to create the visual and musical elements. I’m aware of the ongoing ethical debates surrounding AI and the impact on artists, and I share some of those concerns. However, in this case, generative AI made this project possible. Since the aim is purely educational, with no profit involved, I’m glad that this technology was available to support a project that helps kids learn.

]]>
Comprehension-like syntax in Kotlin https://www.msec.it/blog/comprehension-like-syntax-in-kotlin/ Mon, 11 Nov 2019 07:56:37 +0000 https://www.msec.it/blog-dev/?p=1066 When working with monads it is very common to face situations where multiple nested map and flatMap calls are needed. As an example, let’s take a hypothetical snippet for a Mars Rover kata solution (with some changes just to point out the problem):

val io = printIntroductionText()
        .flatMap { retrieveWorldSizeKm() }
        .map { worldSizeKm -> convertKmToMiles(worldSizeKm) }
        .flatMap { worldSizeMiles ->
            readInitialPosition(worldSizeMiles)
                    .flatMap { pos ->
                        readInitialDirection()
                                .flatMap { dir ->
                                    initState(worldSizeMiles, pos, dir)
                                }
                    }
        }

As you can see given that the initState function requires all the retrieved information from the previous functions, the nesting is needed.

Some other languages, like Scala, provide an alternative syntax for nested flatMap calls. In Scala, you would write:

val io = for {
    _              <- printIntroductionText()
    worldSizeKm    <- retrieveWorldSizeKm()
    worldSizeMiles  = convertKmToMiles(worldSizeKm)
    pos            <- readInitialPosition(worldSizeMiles)
    dir            <- readInitialDirection()
    s              <- initState(worldSizeMiles, pos, dir)
} yield s

This syntax is much more readable and there is much less noise. Unfortunately, Kotlin doesn’t provide any tools like this. While the Arrow team is working on a compiler plugin to provide in Kotlin something similar to the Scala solution, I was wondering if I could find out a way to make the code from the initial snippet at least a bit more readable using plain Kotlin.

Since the first KIO release, I tried to mitigate this problem by offering a particular version of map and flatMap called mapT and flatMapT; these T functions get the result from the argument function and put it in a tuple with the provided input parameter. Using these functions, the initial code would become:

val io = printIntroductionText()
    .flatMap { retrieveWorldSizeKm() }
    .map { worldSizeKm -> convertKmToMiles(worldSizeKm) }
    .flatMapT { worldSizeMiles -> readInitialPosition(worldSizeMiles) }
    .flatMapT { (_, _) -> readInitialDirection() }
    .flatMap { (size, pos, dir) -> initState(size, pos, dir) }

This solution already makes the code much more readable: the nesting has gone. But as the steps number increase, you have to repeat the tuple destructuration; since the tuple size becomes bigger at every step, also the noise generated by this solution increases considerably. Also, even with this solution, you have more noise in the code than in the Scala solution.

So, lately, I started experimenting again to find out some other alternative. After some work, I found out a different solution, that is the following:

val io = 
    printIntroductionText()                 +
    retrieveWorldSizeKm()                   to  { worldSizeKm ->
    convertKmToMiles(worldSizeKm)           set { worldSizeMiles ->
    readInitialPosition(worldSizeMiles)     to  { pos ->
    readInitialDirection()                  to  { dir ->
    initState(worldSizeMiles, pos, dir)
}}}}

I’ve introduced three new elements in the syntax:

  • the + operator is used to sequence effects when the output of the first operand isn’t needed (both if it is of Unit type or not);
  • the to infix function, that extracts the output of the same-row function call and introduces a new variable in the comprehension context with that value;
  • the set infix function, that is equivalent to the to function but must be used if the same-row function call doesn’t return an effect instance (basically the difference is the same of map and flatMap chaining).

In my opinion, the resulting code is much more readable than the code in the initial snippet: the calls sequence is very visible on the left “column” and, to me, this is the most important part of the code; in fact, looking to a piece of code, you should be able to quickly understand what is its purpose and which functions interact with it. Also, there is very little noise if compared with the initial solution or with the flatMapT one.

I’m going to release this new syntax it the upcoming KIO 0.5 release, in addition to the already available standard map/flatMap and tuple-based mapT/flatMapT solutions. I would be very happy to know what do you think about this solution and if you have any suggestion to improve it. As always, you can reach me on Twitter.


P.S.: if you like to auto-format your code with IntelliJ IDEA like me, you can use this feature in order to keep the right formatting for your comprehension code blocks: Exclude code fragments from reformatting in the editor

]]>
Introducing KIO https://www.msec.it/blog/introducing-kio/ Thu, 26 Sep 2019 19:13:00 +0000 https://www.msec.it/blog-dev/?p=1022 Functional programming with Kotlin is sometimes a very delightful experience. But other times, it can be very painful. This is because the Kotlin language sports many features that are very aligned with the functional programming paradigm an so using them becomes very easy. But when trying to follow the same approach that we use with Scala or Haskell, with typeclasses, high order kinds, tagless final and so on, we quickly hit the language limitations. Some libraries, like Arrow, are trying to work around these limitations with some tricks and, in the close future, compiler plugins.

Looking around how different programming languages fulfil the FP needs, I found out that some of them like F# or clojure doesn’t follow the same approach coming from the Haskell world, but instead they just try to stick with the fundamental characteristics of a functional program. Just as an example, the book Domain Modeling Made Functional that explores the functional approach for a business domain application, using F#, doesn’t even mention things like Semigroup, Comonad and so on… It just sticks with the fundamentals. So, I tried to follow the same approach with Kotlin.

What tools do I need?

To me, the fundamental rule for functional programming is the referential transparency, that basically means that you should be able to replace a variable with its definition, or viceversa, and the output of the program (both in terms of result and side effects) shouldn’t change. If you think about it, every other FP requirement comes out from this, like immutability, side effect handling and so on. Other than this, the other important big player in this game is the algebraic data types encoding. If you get these two pillars, you should be able to code entire applications using the FP paradigm.

What Kotlin provides

The Kotlin programming language provides us many tools, let’s summarise most of them here:

  • immutability thanks to the val keyword;
  • product types with data classes and the copy method;
  • sum types with sealed classes;
  • first class functions and high order functions;
  • top-level and generic functions;
  • type-safe optional values with the nullable data types and related operators;
  • tail call optimisation for recursive functions.

What Kotlin doesn’t provide

Given the above list, we are missing basically two things:

  • an easy way to handle and defer side effects (problem usually solved with the IO monad);
  • an alternative way of error handling in substitution to the exceptions throwing/handling.

In order to take care of these two missing pieces, I wrote a very little library: KIO.

What is KIO

KIO is a standalone and lightweight implementation of the IO monad, inspired to the ZIO library for Scala. It basically models a function like:

(R) -> Either<E, A>

Given an input of type R (like what is done with the Reader monad), it provides a result that could be either an error of type E or a value of type A. This is how the KIO type behave:

KIO<R, E, A>

Given this definition, you can also define some derived data types. Directly from the KIO source code:

typealias IO<E, A> = KIO<Any, E, A>
typealias URIO<R, A> = KIO<R, Nothing, A>
typealias UIO<A> = URIO<Any, A>
typealias Task<A> = IO<Throwable, A>
typealias RIO<R, A> = KIO<R, Throwable, A>
typealias Option<A> = IO<Empty, A>

that basically means:

  • IO: an instance of KIO where there is no R injected;
  • UIO: a computation that can’t fail (or at least that isn’t typed for failure);
  • URIO: like UIO, but with the injection of type R;
  • Task: an instance of KIO without R injection and where the error type is set to Throwable;
  • RIO: like Task, but with R injection;
  • Option: an instance of KIO, without R injection, where the error type is limited to only one value that is Empty;
  • you can also create your own combination even if there is no a typealias already provided by the library.

So, with KIO, you can handle side effects like a standard IO monad; also, you can manage errors both typing them as Throwable or with some domain specific sum types. These are the two Kotlin’s missing pieces that we’ve pointed out in the previous paragraph; so with KIO we have completed all the pieces of our puzzle.

In addition, the R injection provides a good way to manage dependency injection, as R could represent both data or functions; we could for example inject some modules (e.g. logging, database access, configuration, …) in the same way we’ve seen here in the previous posts.

A simple example

Here is a simple example of usage of KIO:

import it.msec.kio.*
import it.msec.kio.result.getOrNull
import it.msec.kio.runtime.Runtime.unsafeRunSync

data class Account(val username: String)

fun unsafeRetrieveAccountFromDB(id: String): Account =
        throw IllegalArgumentException("No account found with id = $id")

fun safeRetrieveAccountFromDB(userId: String): Task<Account> =
        unsafe { unsafeRetrieveAccountFromDB(userId) }

fun printToConsole(s: String): Task<Unit> =
        unsafe { println(s) }

fun readFromConsole(s: String): Task<String?> =
        printToConsole(s).flatMap { unsafe { readLine() } }

fun retrieveUsername(userId: String): Task<String> =
        safeRetrieveAccountFromDB(userId)
                .map { it.username }
                .peekError { e -> printToConsole("Warning: ${e.message}") }
                .recover { "anonymous" }

fun interactOnConsole(username: String): Task<String> =
        printToConsole("Welcome $username")
                .flatMap { readFromConsole("Enter some text:") }
                .map { it.orEmpty() }

fun main() {
    val userId = "123"
    val option: Option<String> = retrieveUsername(userId)
            .flatMap(::interactOnConsole)
            .toOption()

    val userInput = unsafeRunSync(option).getOrNull()
    println(userInput)
}

Should I use KIO?

Currently KIO is just a personal project of mine, even if I expect it to be production ready. Also, it must be very clear that KIO follows the approach I described in the introduction, so I’m not going to create another Arrow (that is a very complete and useful library) but just to provide the missing tools that can enable us to experiment with the Kotlin FP fundamentals without depending on a big library like Arrow that, right now, is still under development. Maybe in the future, with the release of Arrow 1.x or with some new Kotlin language features, KIO will become worthless. Right now, with my current FP experience, this is the solution that I trust more in order to write some FP code that I expect to deploy in production.

Where is KIO?

You can find KIO on github here, both source code and maven repository instructions.

Ok, but the documentation?

Currently the only documentation provided are the unit and integration tests and the source code itself. But I’m going to work on it in order to provide some guidance on using KIO and all it provides. Stay tuned!

UPDATE 07/10: the page has been updated with the changes of the 0.3 release that is now available.

]]>
Effect polymorphism with Arrow FX https://www.msec.it/blog/effect-polymorphism-with-arrow-fx/ https://www.msec.it/blog/effect-polymorphism-with-arrow-fx/#comments Thu, 18 Jul 2019 06:35:00 +0000 https://www.msec.it/blog/?p=990 With Arrow FX improving on every Arrow release, we can experiment with it and try to get as much as possible from its innovative approach to side effects handling. In this post, using the modules structure presented in a previous post, we are going to make our code completely independent from the effect implementation (usually IO), making it ready to be used also with other runtime implementations like the ones based on Reactor or Rx. Also, differently from the Tagless Final approach, we’ll be able to do this without even use the Higher Kinded Types feature (if not just in a little utility function).

Note: the features of Arrow used in this post are currently based on what the release version 0.10.0 will be. It hasn’t been release yet, but it will be very soon. If you want to try it, just use the SNAPSHOT builds.

A little recap of how Arrow FX works

If this is the first time you are hearing about Arrow FX, I suggest you to watch this very good introductory talk from Jorge Castillo. Otherwise, let’s just make a very small recap about it. One of the features of the Kotlin programming language is the support for functions marked as suspend; a suspend function can be called only from another suspend function or from a specific context that basically provides a runtime environment where the functions will be executed. When you mark a function this way, the compiler will translate the suspend function to a state machine (this process is described here very well). So, under the hood, when you define a suspend function, you are defining a state machine that describes the computation, and when you call the function you are basically instantiating this state machine. Only when it will process the code, it will generate the real return value of the computation. If you notice, this is the description of a mechanism able to defer side effects and that’s exactly what the IO monad does. So, in Arrow FX, a suspend function returning a value type (e.g Option<String>) can be considered the same of an IO returning the same value type (IO<Option<String>>); also mapping functions to convert between the two cases are provided. So, just to sum up, in Arrow FX effectful functions must be suspend functions, and that’s all.

The side effects handling module

First of all, we need to define a new module that will define how to manage the side effects. We’ll base this module on the Concurrent interface already provided by Arrow; our module name will be HasSideEffectHandling, defined as follow:

interface HasSideEffectHandling<F> : Concurrent<F> {
    suspend fun <A> Kind<F, A>.asSuspended(): A
    suspend fun <A> attempt(f: suspend () -> A): Either<Throwable, A>
}

Basically all the functions needed for the effect handling are taken from the Concurrent interface; but since we need also an abstracted way to convert an effect instance in a suspended function, we are adding the asSuspended extension function in our environment. The attempt function, instead, is just an utility function that is not strictly needed in order to make everything work.

Here is the implementation of this interface for the IO effect:

object IOSideEffectHandling : HasSideEffectHandling<ForIO>,
        Concurrent<ForIO> by IO.concurrent() {

    override suspend fun <A> Kind<ForIO, A>.asSuspended(): A {
        return this.fix().suspended()
    }

    override suspend fun <A> attempt(f: suspend () -> A): Either<Throwable, A> {
        return effect(f).attempt().asSuspended()
    }
}

We are delegating to the IO.concurrent() instance the Concurrent implementation, while our two additional functions are easily implemented using the constructs that IO already provides. We can consider this code as library code; we don’t need to edit it while working on our production code and maybe in the future these functions will be provided directly by Arrow.

An example module with side effects

In order to understand how to use the HasSideEffectHandling module, we can define a Console module that exports just two functions:

interface HasConsole<ENV> {
    suspend fun ENV.printToConsole(s: String)
    suspend fun ENV.readFromConsole(): Option<String>
}

As you can see, both functions are marked as suspend. This means that they will generate a side effect that we are going to defer. In this example, we can consider that the printToConsole function can’t fail, while the readFromConsole function could fail, and in case of failure, we are going to return an empty Option value. Here is a possible implementation:

 interface LiveConsole<F, ENV> : HasConsole<ENV>
    where ENV : HasSideEffectHandling<F> {

    override suspend fun ENV.printToConsole(s: String) {
        println(s)
    }

    override suspend fun ENV.readFromConsole(): Option<String> {
        return attempt { readLine() }
                .fold({ Option.empty() },
                      { Option.fromNullable(it) })
    }
}

First of all, the implementation requires that the environment provides a side effect handling implementation. We need to add an additional generic type F that is the effect we are going to use.

Then, we are implementing the printToConsole function that just calls the println function. The difference between using our printToConsole function and not the println function is that our one is marked as suspend, and so the side effect will be deferred untile the “end of the world” (that usually is the entry point of our program).

The readFromConsole function, instead, has a bit more logic than the other one. Since it is going to generate a side effect, we are marking it as suspended too. Also, we are wrapping the Kotlin standard readLine function inside an attempt block. Attempt basically means “try executing this effectful code and catch potential exceptions, returning an Either instance (right if the execution is successful, left if an exception has been thrown). Then we can fold over the Either in order to create the return value we want.

About the attempt call, I strongly suggest to use it every time something wrong can happen while executing side effects, and then map the Throwable left type into a more type safe error description (like a domain sum type).

Executing the program

A simple program that uses the Console module is the following:

private suspend fun Env.program(): Option<String> {
    printToConsole("Hello")
    return readFromConsole()
}

That can be executed in this way:

fun main() {
    val output = unsafe { runBlocking {
        env.effect { env.program() }
    } }
    println(output)
}

Ok, but we are still missing the environment definition and the corresponding instance.

How to compose the environment

The environment type definition will be the following; this is the first time we specify the real effect type that we are going to use (IO):

interface Env: HasConsole<Env>, HasSideEffectHandling<ForIO>

The definition for this example is quite simple; here is the corresponding composition for getting a real instance:

val env: Env = object : Env,
        HasConsole<Env> by object : LiveConsole<ForIO, Env> {},
        HasSideEffectHandling<ForIO> by IOSideEffectHandling
{}

In order to create the environment, we need to provide an implementation of the HasSideEffectHandling for the IO effect, that is the one we have implemented at the beginning of this post. Obviously, when wiring the LiveConsole module instance, we need to use the same effect we are using for the HasSideEffectHandling module.

Conclusions

With Arrow FX we have defined a module that triggers side effects and handles them in an abstract way, without specifying the concrete effect. So, we can write our code without thinking about Reactor, Rx, IO or any other effect implementation. The, when composing the environment for our application, we can specify the effect we are going to use. This is the same advantage we can get with the Tagless Final approach, but without the burden of Higher Kinded Types, typeclasses over the effect and so on.

You can find the full example source code here.

If you want to leave a comment, send a reply here or send me a direct message.

]]>
https://www.msec.it/blog/effect-polymorphism-with-arrow-fx/feed/ 2
Self-contained example of testing with modules and Arrow FX https://www.msec.it/blog/self-contained-example-of-testing-with-modules-and-arrow-fx/ Fri, 05 Jul 2019 11:23:13 +0000 https://www.msec.it/blog/?p=980 Yesterday I saw this tweet by John De Goes:

So I wondered how the same example could look with Kotlin + Arrow FX, so I ported it. Please consider that I tried to make it as similar as possible to the original one and that some solutions are not production ready, but are ok for just a small example.



If you want to leave a comment, send a reply here or send me a direct message.

]]>
Modular functional programming with Kotlin https://www.msec.it/blog/modular-functional-programming-composition-with-kotlin/ https://www.msec.it/blog/modular-functional-programming-composition-with-kotlin/#comments Tue, 21 May 2019 15:19:03 +0000 http://www.msec.it/blog/?p=842 While introducing myself to functional programming, I needed to understand how all the FP principles connect in order to obtain a production-ready real-life application.

As a backend Java/Kotlin developer, I was missing the structure an application should have in order to be pragmatically functional, without losing all the advantages I’ve got right now in the object oriented world.

It seems very difficult to find this kind of information on the web, or to find out open source codebases in order to take inspiration from, especially in the Kotlin world. So I started studying and experimenting in order to understand how FP applications are structured and composed using other technologies and languages, looking mainly in the Scala world, where the FP community is more mature than the Kotlin one, trying to understand what mysterious definitions like “tagless final” or “free monad” meant.

At the end, what I’ve found out combines hints coming from a very good talk by Paco Estevez, mixed with the work John De Goes is doing with ZIO on Scala and with the lessons from the book “Functional and Reactive Domain Modeling” by Debasish Ghosh.

Why modules

In order to make an application “modular”, you need to split the code in elements that we can call “modules”; in the FP context a simple definition of module could be just “group of functions”.

While trying to find out how to structure a FP backend application with Kotlin, I was looking for a way to create modules in order to be able to test the functions in isolation. Test them in isolation has two meanings:

  1. be able to write tests that stress only the specific functions inside the module and not the whole application;
  2. when a function depends on functions from other modules, be able to replace the dependency with a test implementation or a mock/fake.

So, if you have some code that relies on external services (e.g. in a microservices architecture) or on a database, you could replace the production code that calls the external service or that connects to the database with a test implementation.

How to define a module

So, let’s talk about the code. In order to define a module, you can just define an interface; I use the “Has” prefix here, it will became clearer later:

interface HasGeometricalService {

    fun circleArea(radius: Double): Double

    fun rectangleArea(height: Double, width: Double): Double

}

Then we can implement the functions exposed by the interface, still using an interface as a container:

interface LiveGeometricalService : HasGeometricalService {

    override fun circleArea(radius: Double): Double = 
        PI * radius.pow(2)

    override fun rectangleArea(height: Double, width: Double): Double = 
        height * width

}

also, in this interface we can place all the “private” functions you need, that basically means all the functions you don’t need to expose/export outside this module. Just an example:

interface LiveGeometricalService : HasGeometricalService {

    override fun circleArea(radius: Double) =
            PI * powerOfTwo(radius)

    override fun rectangleArea(height: Double, width: Double) =
            height * width

    fun powerOfTwo(n: Double) =
            n.pow(2)

}

That’s it, the module is complete. But, what happens when a module needs to call a function exposed in another module?

Module’s dependencies

Now, let’s implement a new module that uses the functions exposed by the previous one. Suppose our application is going to get the radius of a circle and the width/height of a rectangle from the user and compute the total area. We define the interface like before:

interface HasTotalAreaService<ENV> {
    
    fun ENV.totalArea(radius: Double, width: Double, height: Double): Double
    
}

There is something different than before in this definition; the interface now defines a generic type ENV and the totalArea function is defined as an extension of it. In this way, we basically say that this method will require some other module in order to make it work, but we are not saying what specific module it will need as it will depend upon the implementation. Also, if you prefer a coherent way to define all your module interfaces, you can use this format for all your modules, as the need for an external dependency is related to the implementation and not on the definition. We can rewrite all the previous code in this way:

interface HasGeometricalService<ENV> {

    fun ENV.circleArea(radius: Double): Double

    fun ENV.rectangleArea(height: Double, width: Double): Double

}

interface LiveGeometricalService<ENV> : HasGeometricalService<ENV> {

    override fun ENV.circleArea(radius: Double) =
            PI * powerOfTwo(radius)

    override fun ENV.rectangleArea(height: Double, width: Double) =
            height * width

    fun powerOfTwo(n: Double) =
            n.pow(2)

}

interface HasTotalAreaService<ENV> {

    fun ENV.totalArea(radius: Double, width: Double, height: Double): Double

}

Now, we can proceed with the implementation of the TotalAreaService module:

interface LiveTotalAreaService<ENV> : HasTotalAreaService<ENV>
    where ENV : HasGeometricalService<ENV> {

    override fun ENV.totalArea(radius: Double, width: Double, height: Double): Double {
        val c = circleArea(radius)
        val r = rectangleArea(height, width)
        return c + r
    }

}

When implementing the module, we keep the environment type (ENV) generic, without resolving it, but we introduce a constraint on it, expressed by the where clause. In this code the where clause stands for “the environment should provide the geometrical service”; if the module requires more than one dependency, you can just list all your dependencies with the syntax:

interface LiveModule<ENV> : HasModule<ENV>
    where ENV : HasService1<ENV>,
          ENV : HasService2<ENV>,
          ENV : HasService3<ENV>

Since the functions implemented inside the module are defined as extensions, all the functions provided by the environment are directly accessible, like the circleArea and rectangleArea used in the example.

Wiring them together

Once we have implemented all our modules, we have to wire them together in order to resolve all the dependencies. We are now going to use a little trick in order to define and instantiate a concrete environment where all our functions are available and every dependency is satisfied.

interface MyEnvironment : 
    HasTotalAreaService<MyEnvironment>, 
    HasGeometricalService<MyEnvironment>

val myEnvironment: MyEnvironment = object : MyEnvironment,
    HasTotalAreaService<MyEnvironment> by object : LiveTotalAreaService<MyEnvironment> {},
    HasGeometricalService<MyEnvironment> by object : LiveGeometricalService<MyEnvironment> {}
{}

First, we define the type of our environment, by creating an interface that basically defines all the modules we are going to wire together. Doing this, we know that the type MyEnvironment would be enough to satisfy all the modules dependencies. Then, we create an instance of the environment combining, for every module defined for the environment, the corresponding implementation we are going to use, taking advantage of the delegation pattern. The little trick I was mentioning before is the self-referential type definition: actually, the MyEnvironment definition contains references to itself. Since this type of definition is allowed in Kotlin, we can make use of it in order to express this composition.

As a side note, you don’t have to create a single environment with all the modules of your application; you could just create different environments composing only the services needed for every specific use case.

Testing

Given this modules structure, it becomes very easy to write unit tests of a module or integration tests between modules just composing the modules under test with their dependencies; and we can choose to use the real production implementation or provide a test one for every single dependency.

So, if we want to unit test the LiveTotalAreaService from the previous example, we can just provide a test implementation of the HasGeometricalService:

interface TestGeometricalService<ENV> : HasGeometricalService<ENV> {

    override fun ENV.circleArea(radius: Double): Double = 5.0

    override fun ENV.rectangleArea(height: Double, width: Double): Double = 8.0
    
}

And, for testing purposes, we can wire the corresponding test environment:

val testEnv: MyEnvironment = object : MyEnvironment,
    HasTotalAreaService<MyEnvironment> by object : LiveTotalAreaService<MyEnvironment> {},
    HasGeometricalService<MyEnvironment> by object : TestGeometricalService<MyEnvironment> {}
{}

Conclusions

Creating a functional application with Kotlin that is both modular and easy to test is possible, even if there aren’t so many examples on the web. I hope this post will be helpful to anyone approaching the world of functional programming with Kotlin.

There are some topics that I didn’t mentioned here, like the side effects handling and the program polymorphism with generic effects; things that are provided with the famous final tagless approach. These can also be provided with this approach (thanks to Arrow), but maybe I’ll talk about them in another post.



If you want to leave a comment, send a reply here or send me a direct message.

]]>
https://www.msec.it/blog/modular-functional-programming-composition-with-kotlin/feed/ 1