Cascade operator as alternative of apply?

In Dart, we have “double dot” called cascade operator to access to few elements of a object without add the variable name:

button
  ..text = "Hello"
  ..id = "mybutton"
  ..class = "style";

That is a good alternative to kotlin apply and its ambiguity

class Foo {
   var baz = "Hello"

  
  fun method() {
     var foobar = Foobar()
    
     foobar.apply {
         name = "My Variable"
         this.baz = baz // this.bar is Foo.baz or foobar.baz? baz is Foo.bar or foobar.baz?
     }
   }
}

With cascade operation, not problem

class Foo {
   var baz = "Hello"

  
  fun method() {
     var foobar = Foobar()
    
     foobar
         ..name = "My Variable"
         ..baz = baz 
   }
}

Interesting idea, but needs a different symbol: .. is already the range operator.

I don’t know. I don’t think this really adds anything to kotlin. I don’t see why a cascade operator would be less ambiguous then apply. Yes you have to understand the concept of receivers, but they are a integral part of kotlin anyways. Adding a new syntax just to replace a function of the standard library does not really make sense to me.
Also what would the return value of your cascade operator be? Should the cascade operator return the instance of foobar like apply does or would it return the value of baz like run does.

2 Likes

May be a funny way to create a tree

class Tree {
    private val nodes = mutableListOf<Tree>()
    fun addNode(node: String): Tree = Tree(node).also { nodes += it }
}

Tree()
  ..addNode("1")
      ..addNode("1.1")
      ..addNode("1.2")
  ..addNode("2")
      ..addNode("2.1")

instead of

Tree()
    .addNode("1").apply {
        addNode("1.1")
        addNode("1.2")
    }
    .addNode("2").apply {
        addNode("2.1")
    }

My personal alternative to apply is also

Nice. Quoting this already shows the point I wanted to make (and changes .. to ...) . This is depending on indentation to make sense. The compiler would have no way of telling what receiver to use in this case.

I think we would use the real builder:

class Tree{
   val nodes = mutableList<Tree>()
   inline fun node(
       node: String, 
       buildTree: Tree.() -> Unit = {}
    ) = tree(node, buildTree).also{nodes += it}
} 

inline fun tree(
    node: String? = null, 
    buildTree: Tree.() -> Unit = {} 
) = (node?.let(::Tree) ?: Tree()).also(buildTree)

tree{
   node("1"){
        node("1.1")
        node("1.2")
   }
   node("2"){
       node("2.1")
       node("2.2")
   }
}

and this is way more powerful and already possible.
So, not convinced about this.
(only the other way around with nesting functions, but there is a post for that :-))

6 Likes
Tree()
...addNode(“1”)
....addNode(“1.1”)
....addNode(“1.2”)
...addNode(“2”)
....addNode(“2.1”)

Pythonic Kotlin :slight_smile:

I agree with @miguelangellv, this overload is a issue, but “double dot” is not a solution.

class Foo {
   var baz = "Hello"

  
  fun method() {
     var foobar = Foobar()
    
     foobar.apply {
         name = "My Variable"
         this@apply.baz = baz
     }
   }
}

with also also not problem:

class Foo {
   var baz = "Hello"

  
  fun method() {
     var foobar = Foobar()
    
     foobar.also {
         it.name = "My Variable"
         it.baz = baz
     }
   }
}

No thank you.

Yes it is, but I think in that case you should use also and maybe even change it to a descriptive name.

I think that using also completely solves this problem, since you can explicitly define a name for the argument instead of it. The other way is to use @-attribution for this. Between those and @DSLMarker, I do not think, that kotlin needs anything else.

1 Like