Cyclical val based initialization

42 views
Skip to first unread message

James Percent

unread,
Jul 20, 2016, 7:54:50 PM7/20/16
to scala-user
Hi, thanks for reading. I have a concrete example at the bottom and the questions are right before the concrete example. I try to summarize before the questions, but I must apologize for being so wordy, if I had more time and I were smarter, I'd always have less to say..

I was thinking/puzzling over how to create a "pure" or "val" based "construction/composition" where class instances reference each other as follows:
A -> B, B->A and A->C, C->A. Ultimately it's to support the following processing chains: B->A->C and C->A->B, encapsulated under 1 set of interfaces, A, B and C. To complicate matters A, B and C are polymorphic specializations, but I want to references to be of type base (if this is confusing I think the concrete example below, which broken, might be more illuminating).

I thought there is no way to do it, and considered breaking the processing chains into 2, building the references bottom up, for example:
Ba->Aa->Ca; for example Ba = Ba(new Aa(new Ca))
Cb->Ab->Bb

But I thought this is very limiting (of course we could use var's for the references, but OMFG I don't want to hear it in code review, ya'know) 

I got excited and thought the compiler knows a trick for this, see below. No dice, it fails gloriously. I have 2 questions that I would be most grateful to get some insight around: 1) is what I'm trying to do actually not possible or did I make a stupid mistake (see the example below), 2) is there some functional programming principle being violated by having the compiler just use a *pointer* to the lazy val below or is it actually very difficult to implement or something?

Thanks in advance!

Example:
import org.scalatest._


trait base {
  def getvalue(): Int
}

trait Vms extends base {
}

trait Edge extends base {
  def getmoq(): Moq
  def getvms(): Vms
}

trait Moq extends base {
}

class VmsImpl(val edge: Edge, val value: Int) extends Vms {
  def getvalue() = value
}

class EdgeServiceImpl(val vms: Vms, val moq: Moq, val value: Int) extends Edge {
  def getvalue() = value
  def getmoq() = moq
  def getvms() = vms
}

class MoqImpl(val edge: Edge, val value: Int) extends Moq {
  def getvalue() = value
}

class Configuration {}

class Factory {
  def createVms(config: Configuration, edge: Edge, value: Int = 0): Vms = {
    return new VmsImpl(edge, value)
  }

  def createMoq(config: Configuration, edge: Edge, value: Int = 0): Moq = {
    return new MoqImpl(edge, value)
  }

  def createEdge(config: Configuration): Edge = {
    lazy val edge: EdgeServiceImpl = new EdgeServiceImpl(createVms(new Configuration, edge, 1), createMoq(new Configuration, edge, 2), 3)
    edge
  }
}

object Runtime extends App {
  val factory: Factory = new Factory
  val edge: Edge = factory.createEdge(new Configuration)
  assert(edge.getvalue() == 3)
  assert(edge.getmoq().getvalue() == 2)
  assert(edge.getvms().getvalue() == 1)
}

Jasper-M

unread,
Jul 25, 2016, 9:27:01 AM7/25/16
to scala-user
I think you will have to make edge lazy all the way down:

trait base {
  def getvalue(): Int
}

trait Vms extends base {
}

trait Edge extends base {
  def getmoq(): Moq
  def getvms(): Vms
}

trait Moq extends base {
}

class VmsImpl(_edge: =>Edge, val value: Int) extends Vms {
  lazy val edge = _edge
  def getvalue() = value
}

class EdgeServiceImpl(val vms: Vms, val moq: Moq, val value: Int) extends Edge {
  def getvalue() = value
  def getmoq() = moq
  def getvms() = vms
}

class MoqImpl(_edge: =>Edge, val value: Int) extends Moq {
  lazy val edge = _edge
  def getvalue() = value
}

class Configuration {}

class Factory {
  def createVms(config: Configuration, edge: =>Edge, value: Int = 0): Vms = {
    return new VmsImpl(edge, value)
  }

  def createMoq(config: Configuration, edge: =>Edge, value: Int = 0): Moq = {
    return new MoqImpl(edge, value)
  }

  def createEdge(config: Configuration): Edge = {
    lazy val edge: EdgeServiceImpl = new EdgeServiceImpl(createVms(new Configuration, edge, 1), createMoq(new Configuration, edge, 2), 3)
    edge
  }
}

Op donderdag 21 juli 2016 01:54:50 UTC+2 schreef James Percent:

Matthew Pocock

unread,
Jul 26, 2016, 7:20:42 PM7/26/16
to Jasper-M, scala-user
Hi James,

You can introduce lazyness as needed, by a combination of making vals lazy, and passing in arguments as suspensions (i.e. as =>T rather than T). However, when I find myself in the situation you are in, and I'm trying to use write-on-modify data structures, it is usually a very strong hint that my data model is not right. It's fine for your data model to contain cycles in the types, but the in-memory graph of instances should always be a DAG, otherwise the nice functional copy-on-modify properties don't really work out well. The most usual place where you introduce vicious cycles is in modelling a relation between two instances by having each instance point to the other one. Don't do this. Either have just one point to the other, or lift the relation out into its own object. There are always tricks to take a nice DAG and on traversing it, harvest the extra information you need to summon the missing pointers as you need them.

Matthew

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Dr Matthew Pocock
Turing ate my hamster LTD

Integrative Bioinformatics Group, School of Computing Science, Newcastle University

skype: matthew.pocock
tel: (0191) 2566550
Reply all
Reply to author
Forward
0 new messages