Referring to abstract type outside of trait for class construction

时间:2018-07-04 23:11:11

标签: scala types functional-programming abstract-type library-design

Premise:

I want to separate the information necessary to instantiate a class from the information necessary to "run" the class. However, the information neccesary to "run" the class may differ from class to class. Thus, I imagine the class "has" specific information to run it, and the two go together.

Here is an example of my code:

trait Machine {
  type Params <: BaseParams

  def start(machineParams: Params): Unit
}

trait BaseParams {
  def speed: Int
  def power: Int
}

class FlyingMachine() extends Machine {
  type Params = FlyingParams

  override def start(machineParams: Params): Unit = {
    println(s"I'm flying with $machineParams")
  }
}

trait FlyingParams extends BaseParams {
  def height: Int
}


abstract class MachineOwner{

  val machine: Machine

  def params: machine.Params

  def startMachine(): Unit = {
    machine.start(params)
  }
}

This compiles, passes tests, I'm happy.

Problem: I'm using val machine: Machine in order to define def params: machine.Params. I've been told to make this a def to let the implementer have more freedom. If I do so, I can no longer refer to machine.Params

At this point, I'm at a loss for how to continue. I keep thinking that if this should be a def and definitely not a val, then my architecture is wrong.

So

  1. Is my approach to this problem wrong, given the premise I set out with?
  2. If it's not wrong, is there a way to still achieve this while using def instead of val in the MachineOwner class?

EDIT Given Alexey Romanov's answer, the last bit of the code would look like this

abstract class MachineOwner{

  type Params1 <: BaseParams

  def machine: Machine { type Params = Params1 }

  def params: Params1

  def startMachine(): Unit = {
    machine.start(params)
  }
}

class FlyingMachineOwner(
  machine: FlyingMachine
) extends MachineOwner {

  override type Params1 = FlyingParams

  override def params = FlyingParams(1,1,1)
}

But this doesn't compile because it expects an override specifically for def machine: Machine { type Params = Params1 }. How does one define that?

1 个答案:

答案 0 :(得分:1)

如果不知道所需的语义,就无法回答。

如果MachineOwner应该拥有一台机器,那么“使它成为def,以使实现者有更多的自由”是不好的建议:它赋予的自由正是从不同的调用中返回不同的机器。 def machine,而不保留对其发出的计算机的引用。

如果应该具有多台计算机,那么它们是否应该都具有相同的Params类型?然后你会做类似的事情

abstract class MachineOwner{

  type Params1 <: BaseParams

  def machine: Machine { type Params = Params1 }

  def params: Params1

  def startMachine(): Unit = {
    machine.start(params)
  }
}

否则,您将需要重新设计,也许是def params(machine: Machine): machine.Params。等等。

进行编辑:您可以执行

class FlyingMachineOwner(
  _machine: FlyingMachine
) extends MachineOwner {

  override type Params1 = FlyingParams

  override def params = FlyingParams(1,1,1)

  override def machine = _machine
}

但是,与使用类型参数得到的结果相比,它确实看起来不必要地复杂。