Lazy way of overriding functions

This time I am just doing pure experimentation. I am sure this methodology of messing up your own code has some fancy scientific or mathematical name. The origin of this is when I was thinking about how limited we are in Android that every time we want to do something with Activity, we have to extend it somehow. And each of us has always experienced that new project starts with creating something called “BaseActivity”. And then later discovering that we need “EvenMoreBaseActivity”, and so on. Until we start begging to be able to actually modify and extend Activity instance itself.

I can’t promise solution to this problem right away (without creating NewFancyBasceActivity), but the technique I am explaining here, can be used for creating your next new shiny kotlin project. This kind of fancy coding is available because we have functions as first class citizens in Kotlin.

Imagine our simple class. It differs from normal classes, that we define our methods as named lambdas.

open class Experimental {
    open var simplePrint = {
        print("One is called")
    }

    open var squaring = { param: Int ->
        param * param
    }
}

And now imagine, that we want to do something additional to our simplePrint or squaring functions. Of course, because our class is open with open vars, we can override the heck out of it, and be done with it. But what if we want something ELSE to be done with the methods. Of course, we create additional extensions, and so on, until we have BasicExperimentalForNetworkRelease version. And… can you parametrize it? Would you be able to create instance of this class (if it would be normal class with functions) with parametrized behaviours?

But because our class has variables which are functions, we can do parametrization. And implement function overriding by introducing two utility functions.

fun modify(old: () -> Unit, new: (original: () -> Unit) -> Unit) = {
    new(old)
}

fun <T, R> modify(old: (T) -> R, new: (T, original: (T) -> R) -> R) =
        { p: T ->
            new(p, old)
        }

First one takes original old function, and lambda, which is being passed original function of type equal to the old function. Also it returns lambda which is the same shape as first function passed to the method.

This gives us fancy way of overriding lambda methods

val instance = Experimental()
instance.simplePrint()
println()
instance.simplePrint = modify(instance.simplePrint) { _super ->
    _super()
    print("****")
}
instance.simplePrint()

Output would be:

One is called
One is called****

Notice that we have modified simplePrint method by creating lambda, which receives original function as it’s parameter, which then we can use in our invocation as we please.

Second modification function is for methods with parameter(s). Actually we need to care only for methods with one parameter, as you can turn any n-parameter function into one parameter function using currying which I will not explain here (ask Google).

fun <T, R> modify(old: (T) -> R, new: (T, original: (T) -> R) -> R) =
        { p: T ->
            new(p, old)
        }

Idea is the same, only we pass the original parameter along with original old function. And the modification code would look something like this:

println(instance.squaring(5))
instance.squaring = modify(instance.squaring) { p, _super ->
    _super(p) + 2
}
println(instance.squaring(5))

Output:

25
27

So here it is. Fancy parametrization and modification of classes if they are written with lambdas not actual functions.

Of course now some thoughts about promised modification of Activity. What you can invent is your own REAL BasicActivity, which has extension points for each lifecycle or other interesting method using this pattern

oepn class MyActivity : Activity() {
  var _stop = {}
  var _start = {}

  override fun onStart() {
    super.onStart() // can't do much about this
    _start() // <- This is where magic happens
  }
  ...
}

Now you can modify your lifecycle behavior on the fly without extending the Activity. You can create instance of the same activity with one behavior, and another instance of the Activity with different behavior. You can have different behaviors for landscape and portrait. Because you can add Activity lifecycle observer on your Application class, you can actually decide those behaviours there and actually INJECT different behaviours to the activities. The same you can do authomatically to your Fragments, if you override onAttach method to your activity, which then can decide (or delegate) what kind of behaviors to inject into your Fragments.

At the end of the day you will have your app consisting not of zillion different classes which actually limit you, but you will have smart classes with zillion of nice small functions doing whatever you need whenever you need without polluting classes.

But… with greate power comes greate responsibility and long hours of debugging.

 

Have fun.

 

List asyncMap() for lazy Kotliniers

Lately going through Clojure async routines and one thing I noticed. These guys has pmap() function for collections. What it does, it makes mapping as parallel work, so gaining some boost on big collections with calculation intensive map function. So me, being lazy and jealous wanted to try if  I can get my 4 cores to do mapping on the kotlin list. If there are some nice libs out there, it is good. I wanted to make the function myself (and probably screw on simple things) but just to see if I can get mapping faster than just plain map.

inline fun <A, B> List<A>.asyncMap(crossinline f: (A) -> B): List<B> { // 1
    val receiver = this // 2
    val cores = Runtime.getRuntime().availableProcessors() // 3
    return if (cores == 1) {
        receiver.map(f) // 4
    } else runBlocking() { // 5
        val results = mutableListOf<Deferred<List<B>>>() // 6
        val chunkSize = Math.max(size / cores, 1) // 7
        (1..Math.min(cores, receiver.size)).forEach {
            val range = chunkIndex(chunkSize, cores, it, receiver)
            results.add(async(CommonPool) { // 8
                receiver.subList(range.first, range.second).map(f)
            })
        }
        results.flatMap { it.await() } // 9
    }
}

So this is the main function.

  1. Define it as extension function for list. I do not want to play with Iterables and collections I can’t “cut” into pieces without overhead. So I choose a list to play, as the algorithm is basically – cut the list into even chunks and give each core to map over this chunk, and then zip them togeather back.
  2. Just convenience
  3. Assuming that our CommonPool for coroutine async will use all the cores, I count them
  4. If we have only one core, there is no much gain I can receive, so just do old map.
  5. We start our coroutine
  6. We will gather our results into chunks being mapped over when we run async. So it is list of Deffered lists.
  7. We calculate what will be the chunk size to work for each core. If the collection size is smaller than the number of cores, well, then we still need to have at least one item to work on.
  8. We start n async mapping tasks where n is the minimum number between cores and size of the collection. You never know if you don’t stick your 2000 sized collection into 2048 core machine.
  9. Flatmapping the results back into original list of mapped results.
fun chunkIndex(chunkSize: Int, count: Int, nth: Int, c: List<*>): Pair<Int, Int> {
    val size = c.size
    return if (nth < count)
        Pair(chunkSize * (nth - 1), Math.min(c.size, chunkSize * nth))
    else
        Pair(chunkSize * (nth - 1), c.size)
}

This is just helper function to get the index of n-th chunk depending on chunk size and cores.

And here is another nice test function you can use not only for mapping stuff, but do some measurements ad-hoc.

fun <T> time(f: () -> T): T {
    val start = System.currentTimeMillis()
    val result = f()
    println("Elapsed:${System.currentTimeMillis() - start}")
    return result
}

Function also is inspired by Clojure guys. It simply prints time the passed block is being executed, returning the result of the block, so you can wrap any function within lambda and measure it’s execution time.

Now the test.

fun main(args: Array<String>) {
    val myList = (0..1000).map { it }

    println("FIRST")
    time({
        myList.map {
            Thread.sleep(3)
            it + 1
        }
    })
    println("SECOND")

    time({
        myList.asyncMap {
            Thread.sleep(3)
            it + 1
        }
    })
}

Output is:

FIRST
Elapsed:3293
SECOND
Elapsed:1734

Process finished with exit code 0

My machine has 4 cores. Program to test simulates “heavy” mapping function, that is that Thread.sleep(3) there. So I almost doubled the time the “heavy” work is being done. This routine will not give you any boost on small collections or even big collections with “light” mapping function. So don’t try to “optimize” your 200 integer list by adding 1 to it. In such case simple map will outperform.

In general this was just an exercise of using coroutines and my computer cores. I am pretty sure I have messed something up and not everything is by kotlin feng-shui but idea is clear that clever tricky tactics of coroutines you can leverage your laziness.

For curious ones : We can do such tricks without worries on concurrency because the math is at play. If our mapping functions are pure functions, we can do mapping in any order we wish and do it in parallel.

Tasks for readers:

  1. Make the algorithm faster and correct 🙂
  2. Refactor it in more feng-shui

Volley for lazy kotliniers

Hi.

As I told in my previous blog posts – I am dinosaur of Android, so I do cling to weird outdated Android concepts and libraries. Today I will wrap Volley into Kotlin coroutines so that one don’t need to think about Volley callback entanglement in each place you need some network request.

And again I start with final code which does the high level request magic.

setContentView(R.layout.activity_main) // 1
queue = Volley.newRequestQueue(this) // 2
launch(UI) { // 3
// 4
    val result = data(queue, "https://jsonplaceholder.typicode.com/posts/1")
    val result2 = data(queue, "https://jsonplaceholder.typicode.com/posts/2")
    val result3 = data(queue, "https://jsonplaceholder.typicode.com/posts/3")
    val result4 = data(queue, "https://jsonplaceholder.typicode.com/posts/4")
// 5
    val textToShow = result.await().toString() + "|||" +
            result2.await().toString() + "|||" +
            result3.await().toString() + "|||" +
            result4.await().toString()
// 6
    mytext.text = textToShow
}
// 7
mytext.text = "Nothing Here"

This is all there is in my activity onCreate() function.

  1. Nothing fancy here, this is setting up my content view, which contains one big fat (match_parent) textfield called “mytext” and I use synthetic plugin for Android (added by default) so I can reference the content with “mytext.text”
  2. This is where my volley leaks into my application. I don’t like it, but for example sake (and coz I am lazy) I leave it here, just to show the demo
  3. We launch coroutine on UI context, this is where we will be setting results of request into our text field.
  4. This is where things get interesting. I invoke my suspending function to make 4 parallel requests into fancy site made just for us – lazy ones – to test our solutions against JSON backend. I suggest to try this… nice place to brainstorm your project network layer. All these 4 requests are backed by Volley, and you can notice that queue is passed to each of these request-making suspending functions. I will show the source below.
  5. We show the text – combination of all 4 responses – into our text field. Notice that we invoke 4 awaits(). Await is actual place where suspension might happen in coroutine (not at “async place). You might think that here are 4 subsequent awaits and suspensions which block while my requests are being done in sequence. But, coz I am lazy – I don’t parametrize async, that’s why it starts on invocation, and I have 4 parallel requests running.
  6. This is where the result of my request is show onto the text field combined by 4 responses. If you run it, the text is not much readable, but you can notice that it is combination of 4 responses.
  7. Because our coroutine might suspend (and it will on network requests) we continue without blocking, and the first thing you will see in the text field is “Nothing here” – to demo, that coroutine “suspends” only inside “launch” part.

Now to the interesting part. The Volley request wrapping into suspending function.

// 1
fun data(queue: RequestQueue?, url: String) = async(CommonPool) { readData(queue, url) }

//2
suspend fun readData(queue: RequestQueue?, url: String): JSONObject = suspendCancellableCoroutine { continuation ->
// 3
    val request = JsonObjectRequest(url, null, Listener {
        println("$url DONE")
        continuation.resume(it) }, // 4
            Response.ErrorListener { continuation.resumeWithException(Exception(it.cause)) }) // 5
    queue?.add(request)
    continuation.invokeOnCompletion { // 6
        request.cancel()
    }
}
  1. This is so called Async-style function which runs the work asynchronously. This is async coroutine builder as you can see. We tell async builder to run on CommonPool context, which will be offUI thread(s) managed by Kotlin. And also you can see, that I am jerk, as I don’t name the method using suggested convention. Such functions should start with “async”, but I leave it to the reader as homework to rename the function. The async-style programming is very powerful. You can offload network or heavy computation work on such functions and still write imperative/functional style code.
  2. This is the meat of the whole example. Finally we define the suspending function, which will do our work. This function takes our volley queue, and requests URL and will return eventually JSONObject. But notice how it is defined. It is defined using kotlin provided construct for making suspensions. You can explore similar method “suspendCoroutine” which returns noncancellable coroutine. I strongly suggest to go inside source code of those functions to see that there is no much magic, but digging deeper you will find a place where the intristic magic happens, which is hidden from us – mere mortals – place where compiler does the hard work. Also explore that this function returns immediately and internally it asks for “result”.  I suggest to explore this part, as you will find out, that coroutine generally returns either data (if they already present) or special value COUROUTINE_SUSPENDED which tells the system to suspend the coroutine. This is why kotlin documentation tells “suspending function might get suspended”. As it might return the data immediately. To understand this better, you might look at the original code, where I have 4 subsequent “awaits()” added in one string. You can imagine, that for first “await” most likely we won’t return from network request and our coroutine will return COROUTINE_SUSPENDED result (which is returned when you just return without special setting of result) and our first await() will stop coroutine. But it will not stop actual network requests being performed, and it might very well happen, that our 1st request ends up to be the last performed, and when the program receives results from 1st await() our subsequent await() already will have the data set and those coroutines will not be suspended as they will have the data.
  3. We create our Volley request as usual, but the interesting parts are JSON response success and failure callbacks.
  4. This is the essence how and when the coroutine “resumes” work. This is success response of our volley request, which does set the result to the continuation passed to us. Here you see the glimpse of so called Continuation Passing Style which in essence is “passing around the parts of your program you want to execute”. In theory this is extremely powerful way of programming, but requires discipline, as with power comes responsibility. So when our volley request returns with successfull results, we will tell the continuation to resume with the passed result, and internally our coroutine will resume.
  5. This is the bad part. As Volley have 2 “callbacks” of the request, we also have 2 ways of handling them. Kotlin coroutines gives us very convenient way of telling that exception happened and this is where we use it. Of course my program does not handle this case, but all I would need to do is to wrap “await” part of the coroutine into try-catch and handle it. Still no callbacks, pure imperative programming. So as I am lazy, I leave this scenario to the readers as homework.
  6. This is interesting part. Because we are building cancelable coroutine, it might happen that we will get cancelled along the line. Which would be normal case in Android life-cycle event. So continuation gives us a way to attach some code when we complete. Because Volley lets us cancel even finished requests, this will work in all cases, but you need to be careful if you can do similar if you don’t use Volley but something else as your backing mechanism. Also notice Kotlin style of programming, that you don’t set some strange “Listeners” as your “finisher” code, but simple lambda. This is another powerful tool how to get rid of all those pesky “listeners” you have in your code.

And that is it.

In addition you can replace your Volley with FireBase, which also has different “callbacks” when invoking requests. Of course to translate this into FireBase you need to play with “single shot” requests. But if you dive into FB deepere, then you can explore channels as part of kotlin coroutines. I hope I will drop a blog post some day how to work with FB “continous” updates of data through coroutines. But the principle will be the same.

DI for lazy kotliniers

Lately I am keen on learning kotlin and all it’s peculiarities. My latest take is on Dagger2. Sorry but there is something I don’t like. Probably the thing that each Activity or Fragment (objects not owned by developers) has to ASK and beg for injection. It is like my activity is asking somebody else to help her after it has been created (instantiated as java object). Might be I don’t like to depend on annotations which are not my nor Androids. I don’t like to be married to frameworks.

So as lazy kotlinier I wanted to explore ways of doing DI in more funny way without dagger but achieving similar cute things. My solution is not close to the depth and possibilities of Dagger, but more like teens cry against parents wisdom. We – children – like to play with homemade toys not fancy cool toys available in shop.

My first thinking was, how to make my Activity not to ask for help explicitly. I didn’t wanted to impose “call” contract on developer, though some “contracting” have to be done. So I invented new contract, which will be encapsulation of my configuration – where all my dependencies comes from.

interface Config {
    val properties : Map<String, Any?>
}

Nice. I have my contract in place. Now my activity can implement it. What else? I need my configuration to live somewhere. So similar as Dagger2 I impose the default location of  config into Application object. In general this is simplification of Module concept of Dagger2, but wha tis Module – some container of all the interesting objects to be injected. As I am lazy – map will do it for me.

class App : Application() {

    override fun onCreate() {
        super.onCreate()
        App.app = this
    }

    companion object {
        var app : App? = null

        fun getConfig() = hashMapOf(
                "myName" to "Jan Kotlin",
                "imageLoader" to object : ImageLoader {
                    override fun loadImage(url: String, view: ImageView) {
                        Picasso.with(app).load(url).into(view)
                    }
                }
        )
    }
}

That’s all my application object. Notice that my configuration contains 2 properties. One is simple string porperty “myName” and other is implementation of ImageLoader, which is abstraction over loading images. And here is the magical interface

interface ImageLoader {
    fun loadImage(url : String, view:ImageView)
}

As you can notice, this interface can be used to implement Picasso or Glide or whatever you like. Important is, that I provide the implementation as one of my properties in the config file. Before we tie all things together we need one simple “bridge” class which does tying our Activity with it’s configuration.

class ConfigDelegate() : Config {
    override val properties: Map<String, Any?> by lazy {
        App.getConfig()
    }
}

Again notice that this is actual implementation of our Config object, which knows where to grab the configuration. This is place where all smart things can happen. Because it is lazy initialization, we can get the config from different places depending on run-time. Also we are able to put this ConfigDelegate different implementations in different flavors of our android application so, different compile targets will provide us with different “meat” of the Config.

And now the main activity which implicitly gets the configuration.

class MainActivity : AppCompatActivity(), Config by ConfigDelegate() {

    private val myName : String by properties
    private val imageLoader : ImageLoader by properties

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        textView.text = myName
        imageLoader.loadImage("http://www.ourhenhouse.org/wp-content/uploads/2013/01/kitten_lily.jpg", imageView)
    }
}

So the magic happens in two places. First we implement Config contract but we delegate it to the ConfigDelegate() instance, which can be implemented and done differently depending on run-time. Second we do implement our instance variables as delegated properties and we delegate them to the Map. In case if you will try to use this approach, make sure that map key names match exactly to the property names of your class.

That’s it. And notice that all important things are still vals not vars. Also we must not expose our internal member properties to outsiders as it is with Dagger2. We don’t need depend on libraries, our code surface is still small and project is not polluted with smart things. Of course you can implement the Config contract with different delegates and use them in different objects for different things. You can dump all your instances into the one config map, or have more fine grained control of different maps delegated to by different objects.

Cheap, dirty and nuclear DI for lazy kotliniers.

p.s. I don’t provide example of activities layout, as it contains one text field and one image view. Anyone can do it. Also don’t forget to add INTERNET permission if you want to copy-paste the example and run it

AsyncTask for lazy kotliniers

Some time ago I was inspired by groovy builders which allowed to write very neat and readable async task constructs.

Fluent.async {
   doSomeWorkOnBackground()
} then {
   updateYourUI()
}

So I got jealous. As it was promised that Kotlin can do similar magic using function literals with receivers. Because I am lazy person I am ready to write some code so I can write less of it later. And the best way to learn new things is to get your hands dirty. And – yes I know – AsyncTask is not the most popular Android construct, but I am oldschool Android dev, so I still love them.

In addition Google spitted out their architecture components to make life easier different for us. So the stage was set, and I wanted to write something to learn a thing or two about functional literals and architecture components at the same time.

I will start with the result. This is what you can do with the small magic.

task<Boolean> { // 1
    attach(this@MainActivity) // 2
    Log.d("ZMP","Starting task")
    findViewById<ProgressBar>(R.id.progress).visibility = View.VISIBLE

    run { // 3
        Log.d("ZMP","Task running")
        Thread.sleep(5000)
        true // 4
    }
    then { // 5
        Log.d("ZMP","Task finished")
        findViewById<ProgressBar>(R.id.progress).visibility = View.GONE
        findViewById<TextView>(R.id.textfield).
            setText("Result is = $result") // 6
    }
}.execute()

What it does? Simple. It executes AsyncTask which does very useful job – sleeps for 5 seconds. But before that, it shows some spinner, after finishing it does update the result. That simple.

Okay. Let’s dive into the actual implementation of the task itself.

fun <R> task(init: Task<R>.() -> Unit): Task<R> {
    val t = Task<R>(init)
    return t
}

This is “entry” function to the task, and it’s name is one you start the task with. This is the line (1) in the code. As per kotlin examples we construct our Task class, which simply extends AsyncTask. Our <R> type is return type of AsyncTask doInBackground() method.  This function returns the newly constructed task to the client where one can decide to pass it somewhere or simply execute.

Constructor of our Task class is as follows:

class Task<R>(val init: Task<R>.() -> Unit) : AsyncTask<Void, Void, R>(), LifecycleObserver {
    private lateinit var background: () -> R
    private lateinit var post: () -> Unit
    var result: R? = null
    private var lifecycleOwner: LifecycleOwner? = null
    private var runWasCalled = false

It accepts the lambda, which is function literal with receiver. This will allow us to access Task methods within the lambda we provide. And running ahead of us, I can tell that what we are providing here is actually body of onPreExecute() method of android AsyncTask.  One will notice that our class implements LifeCycleObserver from android architecture components. As each beginner knows that dealing with AsyncTask is tricky as it does not do any specifics regarding it’s own context. Classical mistake is to start your task in your activity, rotate the screen, and get context memory leaks, exceptions and all the hell gets loose. So we will make our task a bit more smarter and able to manage itself in the context of Activity or Fragment.

Line (2) of the example code is where our task gets attached to the activity (lifecycle owner). The code is simple as everything here.

fun attach(lifecycle: LifecycleOwner) {
    lifecycleOwner = lifecycle
    lifecycleOwner?.let {
        it.lifecycle.addObserver(this)
    }
}

This is method of Task class and all it does, is remembers it’s lifecycle owner and adds itself to it as observer. To be able to detach and cancel itself when the owner gets destroyed, we need to have additional method as per spec.

@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
    this.cancel(true)
}

Method is annotated by ON_DESTROY event, and will cancel the task in a such event. Of course this is just example of how one can react on lifecycle events. In real production environment, you most likely would need more fine grained control over your lifecycle, I am just showing a glimpse of what is possible with android architecture components.

After we have attached, our example lambda proceeds to do some setup work before running background task. All it does, just makes the spinner visible. Nothing fancy here. But remember, that our passed lambda will execute during onPreExecute() method of traditional AsyncTask and here is the body of this method:

override fun onPreExecute() {
    super.onPreExecute()
    init() // execute initialization body passed to the task
    if (!runWasCalled) { // we ensure that run() was called during this call, so we don't have errors then
        throw IllegalStateException("run() must be invoked")
    }
}

This requires some explanation. First line is no brainer, we just call super.onPreExecute() as by generated code. You never know what google will stuff there, so better call it if you have no actual reason not to. But the second line is where interesting starts. If you scroll up, you will notice that “init” is exactly the lambda we are passing into the constructor to the Task class through task function. Here it is where the lambda is called. In reality the lambda does not execute nor background task, nor anything else, it “transparently” is expected to set bodies (lambdas) of those methods. This is why we have a bit more in this onPreExecute() method. Our contract is that we should call “run” method from our initializer lambda, similar as when you extend AsyncTask, the IDE requires you to implement doInBackground() method. Because “run” is “setter” exactly for this method, we must ensure, that this “setter” will be invoked during execution of our initializer lambda. And here comes the actual run method:

fun run(body: () -> R) {
    runWasCalled = true
    background = body
}

Again, we pass lambda to this method, which will be our body of doInBackground(). But we set our runWasCalled instance variable flag to true, so that we know that there will be something actual to run during background execution. And our background execution is again – simple:

override fun doInBackground(vararg p0: Void?): R {
    // we don't care about input params, as usually this is not used
    var r = background() // because result is mutable property we need to do this trick
    result = r
    return r
}

What we do, is execute background instance variable initialized during “run()” method invocation. Because our AsyncTask has “return” type (references by <R>) we have to save this. We have to assign the result to immutable r as our “result” is mutable, and we can’t directly assign and return mutable variable, as it might change in between, but we want to have non null result of our background task invocation. Try to “optimize” it and you will notice IDE complains. So this is what happens on our line (3). We see that body of our doInBackground() is to sleep. What is interesting is line (4) which is return of our execution. Because we defined our Task to return Boolean – line (1) we have to return it from our lambda.

We are almost done. One thing we have to do, is to implement then() method of our Task, which again is “setter”  of onPostExecute() body as function. And here it is:

fun then(body: () -> Unit) {
    post = body
}

All it does – sets instance variable of functional type to it’s passed lambda. So the last thing is to implement onPostExecute() method of our AsyncTask

override fun onPostExecute(result: R) {
    super.onPostExecute(result)
    post() // execute post body
    lifecycleOwner?.let { // we remove ourselves as lifecycle observer
        it.lifecycle.removeObserver(this)
    }
}

Again. It does nothing fancy – executing “post” variable which was set during “then() invocation line (5) of example. Also notice line (6) where we access $result variable. This is done because we do not directly pass “result” from background task into postExecute. We do it behind the scenes through instance variable “result” and we make it accessible, so that our “then” implementation can actually do something with this result.  But in addition, we detach from our lifecycle owner, as we are not possessing any leak danger to our activity or fragment.

Some thoughts and possible improvements for readers to implement and tackle by themselves.

  1. Try to implement progress() method using similar pattern of “setter” of method body and then invoking it. Will you be able to do it through the provided “run” method or you would need to modify it’s parameter to accept functional literal with receiver?
  2. Currently the task method does not invoke “execute()” of async task by itself, but it would be possible, and then you could have “imperative/parallel” programming.
  3. It might be possible to not set result variable during the execution of doInBackground() but just before invoking post() in onPostExecute() when the result is known as it’s parameter.
  4. It can be rewritten to have actual Fluent.async syntax task{}then{}, all you need is to return some intermediate objects which can have proper “run” “then” etc. methods you pas lambdas. The principles of “body setters” would be the same.
  5. For extreme kotlinists. How about introducing * and / operators for Task class, which would implement sequential or parallel execution of several Tasks?