Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Syntax To Initialize Anonymous Type First

I have an anonymous class that needs to be initialized before the trait that it mixes in. Early initialization won't work because they don't allow one to use the 'this' reference. I can make it work if I declare the class as an inner trait with a self type, but this seems unnecessarily verbose, as the type is only used in one place in the code and is inuitive to be inlined as an anonymous class. However, I seem to find the syntax that scala will accept and meets the initialization order requirements that I have. Here is a simplified example without the extraneous detail (assume there are reasons I'm doing things this way).

trait WaitCondition[+T] {
...
}

trait EventWaitCondition[+T] extends WaitCondition[T] {
...
}

trait Event { outer =>
    private[this] var _cachedWaitCondition : Option[WaitCondition[T]]

    def next() : WaitCondition[T] =
        //Is there a way to "inline" the defintion of NextWaitCondition
        //without screwing up the initialization order?
        _cachedWaitCondition.getOrElse{ new NextWaitCondition with EventWaitCondition[T] }

    private[this] trait NextWaitCondition { this : WaitCondition[T] =>
        outer._cache = Some(this)
        ....
    }
    ....
}

So, basically, my question is that is there a way to inline the definition of NextWaitCondition as an anonymous type without changing the initialization order between NextWaitCondition and WaitCondition (i.e., so that NextWaitCondition still initializes first)?

like image 501
Nimrand Avatar asked Dec 18 '25 01:12

Nimrand


1 Answers

Short answer: of course not.

We must take your word that there is just cause for the bending the laws of init order. Ponder for a moment the souls who respect the law but still suffer under it.

@xiefei's answer is not kludgy; it's sufficiently structured to effect the kludge you seek.

There is talk about deprecating DelayedInit in favor of a postConstructor hook; but you're really asking for a preConstructor hook, so why not just formalize that with a template method?

And depending on the dependency between your Foo and SubFoo, this could be a matter of preferring composition to inheritance. Then there are no games with init order.

In the following, Firstly generalizes your named solution with a template method. It has the advantage that only the use site knows or cares about it.

trait Second {
  println("I must happen after first.")
}
trait SecondDelayed extends DelayedInit {
  println("SecondDelayed neutral stuff")
  def delayedInit(body: =>Unit) {
    body // body first
    println("I must be delayed after first.")
  }
}
trait Firstly {
  def firstly
  firstly
}

object Test extends App {
  def trial(t: =>Unit) {
    println("----")
    t
  }
  // candidate for least obnoxious
  trial {
    new Firstly with Second {
      def firstly {
        println("Do this firstly.")
      }
      println("Don't care when this happens.")
    }
  }
  trial {
    // current solution
    new Something with Second
    trait Something { this: Second =>
      println("First code.")
    }
  }
  trial {
    // prefer anon
    new Second {
      println("Anon first?") // nope
    }
  }
  trial {
    // DelayedInit solution
    new SecondDelayed {
      println("Anon first, then other delayed.")
    }
  }
  trial {
    // the "delayed" code must be idempotent,
    // or find a useful way to trigger execution;
    // here, the delayed code happens twice.
    class Foo extends SecondDelayed {
      println("Foo wants init, too")
    }
    new Foo {
      println("Anon first, then other delayed.")
    }
  }
  /* early defs are only for defs, with no this
  new {
    println("Anon first.")
  } with Second
  */
  trial {
    // trait code doesn't participate
    new DelayedInit with Second {
      def delayedInit(body: =>Unit) {
        println("My crucial early init business")
        body
      }
    }
  }
}
like image 145
som-snytt Avatar answered Dec 20 '25 04:12

som-snytt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!