动机:
(与该主题没有紧密关联,但说明了一个用例。可跳过)
我Scala.js
用来编写React代码。我现在有一个form
包含一堆input
s,每个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)
}