0

Is it possible to set a variable which has scope in a method from it's child class in scala?

Why I need is explained in following code snippet:

abstract class SuperTask {
  def doWork():Unit = {
     var taskData:Option[TaskData] = None
     try {
        _actualWork() 
     }
     catch {
      //  catch exception and do reporting using taskData variable
     } 
  }

  protected def _actualWork(): Unit
}

class FunTask extends SuperTask {
    override def _actualWork(): Unit = {
       //This will have data for taskData
       //PROBLEM is how to set taskData of parent class from here so that if this throws exception, I can do reporting from catch in doWork method.
    }
}

I want to set value of taskData variable, so that if _actualWork fails, I can have proper reporting, retries, etc which can be common for all child classes of SuperTask class.

I can not define taskData variable at SuperTask class level as it then will be shared and fail in case of concurrency.

Saurabh
  • 71,488
  • 40
  • 181
  • 244
  • 2
    I'm not sure that I understand correctly, bu you can change a signature of the `_actualWork()` function and pass `myObject` to it. Like: ` try {_actualWork(myObject) } catch {}` – dyrkin Aug 08 '18 at 12:22
  • @dyrkin No, That is not possible: https://stackoverflow.com/q/9535821/1610034 – Saurabh Aug 08 '18 at 12:23
  • 1
    So, do you want to assign new value to `taskData` from `_actualWork` ? – dyrkin Aug 08 '18 at 12:25
  • @dyrkin Yes, I want to set `taskData` variable, so that if `_actualWork` fails, I can have proper reporting, retries, etc which can be common for all child classes of `SuperTask`. – Saurabh Aug 08 '18 at 12:28
  • Why don't you split then `_actualWork` into two functions? 1st `getData` and the second one is `_actualWork`. Before try block you can call `getData` which can be implemented in the child class – dyrkin Aug 08 '18 at 12:32
  • 5
    This kind of sounds like the [XY problem](https://en.wikipedia.org/wiki/XY_problem). Can you elaborate more on what you're trying to achieve instead? – Yuval Itzchakov Aug 08 '18 at 12:34

2 Answers2

1

Just pass a closure which knows how to set taskData to _actualWork:

type TaskData = String

abstract class SuperTask {
  def doWork():Unit = {
    var taskData:Option[TaskData] = None
    try {
       _actualWork(td => taskData = td) 
    } catch {
      case _: Throwable => println("last task data: " + taskData)
    } 
  }

  protected def _actualWork(setTaskData: Option[TaskData] => Unit): Unit
}

object FunTask extends SuperTask {
  override def _actualWork(setTaskData: Option[TaskData] => Unit): Unit = {
    // No problem to set taskData of parent class from here:
    setTaskData(Some("Hello, world!"))
    throw new Error("error")
  }
}

If you now invoke doWork on FunTask, which sets the taskData and throws an error, you get the following output:

FunTask.doWork() 
// output: last task data: Some(Hello, world!)

Note that this is merely a really cheap variant of the "Observer"-pattern, where the state of your registered "observer" consists of a single local variable, and the "notifications" consist of invocations of the closure setTaskData. You could just as well pass a whole bunch of proper "observers" into the _actualWork method, so that they can monitor what's going on inside _actualWork.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • Thanks, This will work for me. Do have any good link showing examples of observer pattern of this type in scala? – Saurabh Aug 08 '18 at 13:33
  • @Saurabh Not really. It's rather just a very vague analogy. There is just some object-like entity (a closure, in this case), and you can say to it "look, something changed!". That's all there is to it... – Andrey Tyukin Aug 08 '18 at 13:35
0

This regarding my last comment below your message:

Split _actualWork into two functions? 1st one is Option[TaskData] and the 2nd one is _actualWork(taskData: Option[TaskData]).

abstract class SuperTask {
  def doWork():Unit = {
     val taskData:Option[TaskData] = getData
     try {
        _actualWork(taskData) 
     }
     catch {
      //  catch exception and do reporting using taskData variable
     } 
  }

  protected def getData(): Option[TaskData]
  protected def _actualWork(taskData: Option[TaskData]): Unit
}

class FunTask extends SuperTask {
    override def getData(): Option[TaskData] = Some(data)
    override def _actualWork(taskData: Option[TaskData]): Unit = {
       //do some work with taskData
    }
}
dyrkin
  • 544
  • 4
  • 15