解决斯卡拉鸡蛋问题的惯用方法

时间:2015-10-03 01:52:53

标签: scala

如何很好地初始化这样的结构:

case class A(name: String, b: B)
case class B(name: String, a: A)

寻找没有延迟val的解决方案(性能开销)并且不向现有案例类添加新成员(它看起来很丑陋),但特殊封装器和原始类型签名的更改可能没什么问题(至少我是最好的)得到了)。 toString-problem可以忽略不计,因为我可以在某些特征中覆盖它。

2 个答案:

答案 0 :(得分:0)

现在,我想出了这个:

   case class Chicken[T](h: Holder[T, _]) {
     def get = h.chicken
     override def toString = get.toString
   }

   case class Egg[U](h: Holder[_, U]) {
     def get = h.egg
     override def toString = get.toString
   }

   implicit def egg[T] = (_: Egg[T]).get
   implicit def chicken[T] = (_: Chicken[T]).get

   case class Holder[U, T] (chickenF: (Egg[T], Chicken[U]) => (U, T)) {
     val (chicken, egg) = chickenF( Egg(this), Chicken(this))
   }

   def mutual[U, T](chickenF: (Egg[T], Chicken[U]) => (U, T)) = {
        val h = new Holder(chickenF)
        h.chicken -> h.egg
   }


   trait Named { //to avoid unfinite toString
       def name: String
       override def toString = name
   }

用法:

case class A(name: String, b: Egg[B])
case class B(name: String, a: Chicken[A]) extends Named

val (a, b) = mutual[A, B](A("a", _) -> B("b", _))

val (a2, b2) = mutual[A, B]{ (b2, a2) => //alternative for complex cases
    A("a", b2) -> B("b", a2)    
}

println(a)
println(b)
println(a.b)
println(b.a)

结果:

A(a,b)                                                                                                                                                                                                                                                 
b                                                                                                                                                                                                                                                      
b                                                                                                                                                                                                                                                      
A(a,b) 

仍然希望有一个库,比如scalaz或基于宏的解决方案。

答案 1 :(得分:0)

我找到一段时间来处理这个问题的另一种方法(虽然我现在更喜欢设计我的类型,这些循环引用没有出现),是传递一个生成器函数来获取这些值,而不是值直接(或替换为您的ChickenEgg类型)。例如:

trait Named { //to avoid infinite toString, as before
  def name: String
  override def toString = name
}

case class A(name: String, generator: A => B) extends Named { val b: B = generator(this) }
case class B(name: String, a: A) extends Named // Either or both types could extend Named

使用示例:

scala> def generator(name: String)(a: A): B = B(name, a)
generator: (name: String)(a: A)B

scala> val a = A("a", generator("b"))
a: A = a

scala> a.b
res6: B = b