4

动机:

(与该主题没有紧密关联,但说明了一个用例。可跳过)

Scala.js用来编写React代码。我现在有一个form包含一堆inputs,每个onChange callback在它的道具中都需要一个。请参阅受控组件

case class Input(info:String,value:String,onChange:(e:Event)=>Unit)

问题是:info它与 无关React,但一直与它耦合。我会将控制逻辑与 Web 内容分开。所以我做了:

trait Control[T]{val value:T,val onChange:(e:Event)=>Unit}
case class Input(info:String)

//when need to create a controlled props:
private trait TextInputCtrl extends Control[String]{//..implementation}
new Input("some info") with TextInputCtrl

这只解决了一半的耦合问题。因为TextInputCtrl必须在 React 组件中编写实现才能访问状态。也一样new Input(...),除非TextInputCtrl公开。但是如果你这样做了,你就会在外面携带 React 逻辑。

出色地。如果 trait 可以动态混合到 instance中。Input我可以在其他地方创建一个不受控制的干净实例,并将其传递给 React 组件。灵活得多。这甚至提供了以下杠杆作用:

Seq(input...).map(_ with XXControl)

所以,我做了一些尝试:

问题:

我想使用这个简单的哲学:

def dynamix(in:Input):Input with Control{
  new Input(in.info) with Control{//..implementation}
}

但通过宏:

 q"""
   new $MixinToolSym[$tpeC,$tpeT]{
       def dynamix(in: $tpeC): $tpeC with $tpeT = {
          new $tpeC (..$constrBody) with $tpeT
       }
   }
 """

我达到了成功的编译。但在运行时失败:

trait MixinTool[C, T] {
  def dynamix(in: C): C with T
}

object MixinTool {
  implicit def materialize[C, T]: MixinTool[C, T] = macro MacroImpl.impl[C, T]
}


val mt=MixinTool.materialize[Input,Control]
mt.dynamix(inputInstance) //error

它抛出:

java.lang.IllegalAccessError: 
tried to access field x.x.Input.info from class x.x.X$$anon$3$$anon$2

有什么问题?

这不是问题scala App

==================================================== =

讨论达到脱钩目标的其他方法:

其他方法:(但可能不是最好的解决方案

下面两者都失去了自我类型特征直接访问其他字段的能力。并且难以增量链接或部分覆盖控制逻辑。

case class PropsContainer(input:Input,control:Control = Control.empty)

或者:

case class Input(info:String,value:String="",onChange: Control = Control.empty)
def addControl(input:Input,control:Control):Input{
  input.copy(onChange = control)
}
4

0 回答 0