Extension functions in extension functions?


#1

I love Kotlin's extension functions ... you can define a method in a class like this:

class Foo(val fooness: String) {   fun foo() = println("My fooness is so "+fooness)    }

Or you can define it as an extension like this ...

class Foo(val fooness: String)

fun Foo.foo() = println("My fooness is so "+fooness)

Does the same, which is great. You can even define extension functions to one class inside another class …

class Foo(val fooness: String)

class Bar(val barness: String) {
  fun Foo.bar() = println("My fooness is so “+fooness+”, and my barness is so "+barness)
}


Which means while you are inside the Bar class you can pretend that any Foo has a .bar method … awesome …

So the question is … can you define that function as an extension outside Bar?

class Foo(var fooness: String) class Bar(val barness: String)

fun Bar.Foo.bar() = println("My fooness is so “+fooness+”, and my barness is so "+barness)

This syntax doesn’t work … but in theory there is no reason why not … Bizarrely enough I actually have a Kotlin project I’m working on where it would be quite useful to be able to do this (no really!). So …

  1. Does anyone know if you can do this in Kotlin in some way?
  2. If not ... why not! :)

NOTE: yes this is all slightly ridiculous ... but it's quite fun to see how far you can push it ;)


#2

fun Foo.bar(val bar: Bar) = println("My fooness is so "+fooness+", and my barness is so " + bar.barness)


#3

My question is slightly tongue in cheek, it's obviously messing around in the fringes of the language design :)

Well here is my actual motivating example … it’s using type safe builders to do pretty printing …

import java.util.ArrayList

abstract class AST {
  protected fun PrettyPrinter.listInBrackets(items: List<String>) {
  +"("; +items.makeString(); +")"
  }

  abstract fun pretty(): String
}

class MethodAST(val name: String, val formalParams: List<String>): AST() {
  override fun pretty() = prettyPrinter {
  +"fun "; +name; listInBrackets(formalParams)
  }
}

fun prettyPrinter(f: PrettyPrinter.() -> Unit): String {
  val printer = PrettyPrinter()
  printer.f()
  return printer.toString()
}

class PrettyPrinter {
  val output: ArrayList<String> = arrayListOf()

  fun String.plus() { output.add(this) }
  fun toString() = output.makeString("")
}

fun main(args: Array<String>) {
  val method = MethodAST(“foo”, listOf(“a”, “b”, “c”))
  println(method.pretty())
}

The above works fine. Note that this is a bit overkill for just producing strings … but for the actual code I’m working with I have a much richer return representation than String so it’s worth the effort.

Now the interesting bit is the extension method in AST: listInBrackets. Note that ‘listInBrackets’ is not defined inside PrettyPrinter, that’s because the pattern in ‘listInBrackets’ is common to my ASTs, but it doesn’t necesarily belong in the much more general PrettyPrinter library.

So what I’m curious to know is whether you can achieve this:

class MethodAST(val name: String, val formalParams: List<String>): AST() {   override fun pretty() = prettyPrinter {   +"fun "; +name; formalParams.inBrackets()   } }

Now you can obviously do this by moving the ‘inBrackets’ method into the PrettyPrinter class …

class PrettyPrinter {   val output: ArrayList<String> = arrayListOf()

  fun String.plus() { output.add(this) }
  fun toString() = output.makeString("")

  fun List<String>.inBrackets() { +"("; +this.makeString(); +")" }
}

But I don’t want to do that ‘inBrackets’ is specific to my AST classes, it doesn’t belong in PrettyPrinter! What I want to do is define it in the AST class …

abstract class AST {   protected fun PrettyPrinter.List<String>.inBrackets() {   +"("; +this.makeString(); +")"   }

  abstract fun pretty(): String
}

Hence the question: can you define an extensions to class List<String> as a member of class PrettyPrinter, inside class AST? I strongly suspect the answer is no.

And its fine if the answer is ‘no’, clearly none of this is strictly necessary for my purposes: the solution at the top of the page works well enough. I doubt many people will run into difficulty if it is impossible to define “member extensions” as extensions, it’s probably not even worth the extra syntax clutter that it would introduce. Still the lack of orthogonality is slightly grating :wink:


#4

1. No 2. No need.

Note: extensions and members are not at all the same.


#5

That's probably the right answer :)

Personally I’m quite glad that Kotlin is taking a ‘middle road’ approach on the type system. The type system is there, and it can help you, and you can do some great stuff with it … but it’s not super complex or difficult to understand.


#6

Sorry to revive this 2 year old thread but I was coming here to post something very similar and disagree with the resolution of this thread.

The comment of “No need” by @abreslav is worthless without some reasoning behind it. Why is there no need? Is there a way to accomplish this some other way?

The suggestion by @danseid is to not make it an extension function and just make it a a regular function but that changes the syntax. By that logic there are lots of things in Kotlin for which there is “no need”. There is “no need” of extension functions since you can just create a regular function and pass the target as a parameter. There is “no need” of infix function as you could just add the dot and the parentheses.

But there is a need for these things as they help make code more readable and expressive.

In my case I have scoped infix extension functions:

class Foo
{
    fun f(v1:Int, v2:Int) { TODO() }

    infix fun Int.bar(other: Int) = f(this, other)
}

fun main(args: Array<String>)
{
    Foo().run { 5 bar 7 }
}

and I want the ability to create additional infix functions (e.g. a baz function) that I can call similarly without requiring them to be defined inside of Foo.

Sure, I could make it a regular function so that it is baz(1, 2) instead of 1 baz 2, but code I like Yoda do not.

So I think this feature actually is needed and am looking for some justification why it should not be added.


#7

Sorry for digging out this question, but i think I can add something

I was just searching Internet about same as original poster, and came to conclusion what language could do for us.

To begin, I’m Android developer and started to use Kotlin 2 months ago.

I would love to extend library class with member extension functions, like:

extend Activity {
    fun Float.dp() = this@Float / this@Activity.resources.displayMetrics.density
}
// then inside class extending activity we could call:
10.dp()

and after some thinking about how extensions work I have conclusion that this would not work as intended (we can’t scope extension to be callable only from Activity, as we are not part of it). Instead language would let as do as follow:

define global extension

fun Float.dp(activity: Activity) = this / activity.resources.displayMetrics.density

this would give us call like:

//inside class extending Activity
10.dp(this)

and when we call such method in class that extends one of parameters, it would be passed automatically instead of passing this

//this@activity is passed automatically
10.dp()

this way we still have global extension method, but also we have some kind of extension function like defined inside class. Moreover it would also work this way if we have more this@class to be passed as parameters.


#8

Yes that won’t work for a class that you do not have control of. If you did have control of the class you could make it a member extension function.

Here is what they did it in the Anko library which lets you do dip(10) in any view, fragment, or context class: