4

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.

metch
  • 673
  • 7
  • 14

2 Answers2

3

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.

0__
  • 66,707
  • 21
  • 171
  • 266
3

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!"))
Community
  • 1
  • 1
Anthony Accioly
  • 21,918
  • 9
  • 70
  • 118
  • Thanks, the `Dynamic` trait is what I was looking for ! The `()` in your last example are a bit annoying... It would be great to write only `showFruit apple`. I will try to use an external DSL that is more flexible. – metch Mar 24 '14 at 20:20