metch
metch

Reputation: 682

Object methods call without parentheses

Is it possible to call an object method without parentheses, after import it ?

Here is my test code, based on this article by Martin Odersky:

package gardening.fruits

object PrintPlanted {
  def main(args: Array[String]) {
    // Call method on object
    import gardening.fruits
    fruits showFruit(apple)
    fruits showFruit apple

    // Import object
    import gardening.fruits._
    showFruit(apple)

    // showFruit apple

    // Error: missing arguments for method showFruit in package fruits;
    // follow this method with `_' if you want to treat it 
    // as a partially applied function
  }
}

After importing the package object fruits, I can call the method showFruit(apple), but is there any way to call the method without parentheses like the last of the code ?

I use such of methods in a little DSL and it is a bit annoying to use parentheses. All your suggestions are welcome.

Upvotes: 4

Views: 956

Answers (2)

Anthony Accioly
Anthony Accioly

Reputation: 22471

Well, almost anything in Scala is doable:

package object fruits {
  val planted = List(apple, plum, banana)

  import scala.language.dynamics

  object showFruit extends Dynamic {

    private val lookupFruit = planted
        .map(f => (f.getClass.getSimpleName, f)).toMap;

    def applyDynamic(name: String)(args: Any*) = {
      realShowFruit(lookupFruit(name+"$"));
    }

    private def realShowFruit(fruit: Fruit) = {
      println(fruit.name + "s are " + fruit.color)
    }

  }
}

Will make your code work.

HOW?

We are replacing the original showFruit method with an object. This object happens to be Dynamic, so:

showFruit apple ~~> showFruit.applyDynamic("apple")([Some param, see below])

Which means that we are not receiving the apple object, we are dealing with an "apple" (String).

So we need to lookup fruits according to their string names. I'm using a map and some nasty Java Reflection for this :). The apple object is actually gardening.fruits.apple$, so our f.getClass.getSimpleName approach works.

Well, now that you know how to do what you want, please don't. This is actually a really broken workaround simulating post fix operator syntax (which is, by itself, not advisable) .

This will put you in all sorts of trouble. Say you want to use your method like this:

def main(args: Array[String]): Unit = {
    import com.sevenrtc.testreflection.fruits._
    showFruit apple // won't compile
}

But this compiles:

def main(args: Array[String]): Unit = {
    import com.sevenrtc.testreflection.fruits._
    showFruit apple 
    println("Will compile!");
}

Why? For the same reason that this compiles:

def main(args: Array[String]): Unit = {
    import com.sevenrtc.testreflection.fruits._
    showFruit apple 
    ()
}

The result of println (which is ()) is actually being passed to applyDynamic. So the first example is equivalent to:

showFruit.apple(println("Will Compile!"))

Upvotes: 3

0__
0__

Reputation: 67290

No that is not possible. e1 op e2 is a so-called infix operation and equivalent to e1.op(e2). This does not work when the receiver (e1) is left out. For the same reason you can't write things like println "hello", either.

Upvotes: 3

Related Questions