在非案例类中使用镜头使用Scala中的构造函数扩展内容

时间:2017-03-28 02:43:39

标签: scala scalaz lenses

我可能正在考虑这个错误的方法,但我在Scala中遇到麻烦,在使用构造函数扩展内容的类上使用镜头。

class A(c: Config) extends B(c) {
    val x: String = doSomeProcessing(c, y) // y comes from B
}

我正在尝试创建一个Lens来改变这个类,但是我很难这样做。以下是我希望能够做到的事情:

val l = Lens(
    get = (_: A).x,
    set = (c: A, xx: String) => c.copy(x = xx) // doesn't work because not a case class
)

我认为这一切都归结为找到改变这门课程的好方法。

我有什么选择来实现这样的目标?我在两个方面考虑这个问题:

  • 将初始化逻辑移动到伴随A对象中def apply(c: Config),并将A类更改为从伴随对象创建的case class。很遗憾,我无法从B(c)中的object延伸,因为我只能在其c方法中访问apply
  • x成为var。然后在Lens.setA.clone中,然后设置x的值,然后返回克隆的实例。这可能会起作用,但看起来很丑陋,更不用说将其更改为var可能会引起一些人的注意。
  • 使用一些反射魔法来复制。如果我能避免这种做法,并不是这种做法的粉丝。
你怎么看?我是否认为这是错误的方法,还是有解决这个问题的简单方法?

1 个答案:

答案 0 :(得分:1)

这取决于您对Lens的期望。 Lens定律指定setter应该替换getter将获得的值,同时保持其他所有内容不变。目前还不清楚这里的其他一切是什么意思。

您是否希望在设置时调用B的构造函数?您调用了doSomeProcessing方法吗?

如果您的所有方法都是纯粹的功能,那么您可能会认为课程A有两个“字段”,c: Configx: String,因此您可以将其替换为这些字段case class。但是,这会在尝试仅使用c作为参数实现构造函数时导致问题。

我会考虑做以下事情:

class A(val c: Config) extends B(c) {
  val x = doSomeProcessing(c, y)
  def copy(newX: String) = new A(c) { override val x = newX }
}

您编写的Lens现在完全有效(copy方法中的命名参数除外)。

如果A中的其他属性依赖于x,请注意,这可能会为这些属性创建一个具有意外值的实例。

如果您不希望c成为类A的属性,那么您将无法克隆它,或者在不给Config的情况下重建实例你的建造者,Lens生成器不能拥有,所以看来你的目标是无法实现的。