Harry
Harry

Reputation: 709

Using Scala trait as a callback interface

I just started learning scala and trying learn myself by making a simple application, dicebot.

Here's the simple application which process this simple command.

CommandRouter().run("dice roll 3d4")

package com.kwoolytech.kwoolybot

case class CommandRouter() {

  def run(command: String): List[String] = {
    val _command = command.split("\\s+").toList

    commandArray.head match {
      case "dice" => Dice.run(_command.tail)
      case _ => List[]
    }
  }
}


package com.kwoolytech.kwoolybot

case class Dice() extends Bot {

  def run(command: List[String]): List[String] = {
    command.head match {
      case "roll" => roll(_command.tail)
      case _ => List[]
    }
  }

  private def roll(command: Array[String]): List[String] = {
    val rollCmdPattern = "([0-9]+)d([0-9]+)".r

    command.head match {
      case rollCmdPattern(numTry, diceSize) => rollDice(numTry, diceSize)
      case _ => List[]
    }
  }

  private def rollDice(numTry: Int, diceSize: Int): List[String] = {
    (1 to numTry).map { x => scala.util.Random.nextInt(diceSize) + 1})
  }.toList.map(_.toString)

}

Btw, at the very first part of the application, I want to pass over a callback function, which will get called when the Dice Bot done its work.

Dice.run(_command.tail, callback)

The thing is.. I am not quite sure what to pass over for the callback parameter. If this was Java, I will define a interface like below but in scala, I am not really sure what to use.

interface KwoolyHttpCallback {
    void onWeatherJsonDataReceived(String result);
    void onWeatherBitmapDataReceived(Bitmap result);
}

private void initializeKwoolyHttpCallbackFunction() {
httpClientCallback = new KwoolyHttpCallback() {
    @Override
    public void onWeatherJsonDataReceived(String result) {
       try {
            dataModel.setWeatherJsonData(result);
            queryWeatherBitmapData();   
       } catch (Exception e) {
            Log.e(getClass().getName(), "Exception: ");
            e.printStackTrace();
       }
   }

I heard from somewhere that the trait is the interface but I am really not getting it.

trait BotInterface {
  def onReceiveResult(result: List[String]): List[String]
}

Could you please teach me how to use the trait as a callback interface?

Thanks in advance! :)

Upvotes: 0

Views: 357

Answers (1)

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149538

If what you need is a callback taking a List[String] and returning a List[String], there's no need for a trait, you can require a function:

def run(command: List[String], callback: List[String] => List[String]): List[String]

The callback is using a little syntax sugar for the definition. It actually gets translated into a type named Function1[List[String], List[String]].

Now callers can use anonymous function syntax to pass in an implementation:

run(List("1","2","3"), l => { 
  l.foreach(println)
  l
})

We can even make this a little bit prettier by using a second parameter list (this is called currying):

def run(command: List[String])(callback: List[String] => List[String]): List[String]

And now we have:

run(List("1","2","3")) { l =>
  l.foreach(println)
  l
}

If you're still convinced that you want a trait, it's actually pretty similar to how you'd define an interface in Java:

trait BotInterface {
  def onReceiveResult(result: List[String]): List[String]
}

def run(command: List[String], callback: BotInterface): List[String]

Note that traits in Scala can have default implementations, similar to Java 8's default methods:

trait BotInterface {
  def onReceiveResult(result: List[String]): List[String] = result.map(_.toLowerCase)
}

And inside the run definition you'll need to call callback.onReceiveResult(list)

Upvotes: 1

Related Questions