trying to avoid huge lists of type parameters - is this a "bakery of doom" problem

32 views
Skip to first unread message

Tim P

unread,
Dec 17, 2011, 7:00:47 AM12/17/11
to scala-user
Hi
I've got a bunch of types (sites, products, orders, order parts,
vehicles etc.) which all need to be subclassed to add additional data
and constraints. Since they are all interlinked I end up with
something like

Order[Site, Product, OrderPart[Product]] or worse

Consignment[Site, Product, OrderPart[Product], Order[Site, Product,
OrderPart[Product]]] and so on.
A long and tedious list of type parameters.

Lots of things reference the generic types. For example
Delivery[Site, Consignment[Site, Product, OrderPart[Product],
Order[Site, Product, OrderPart[Product]]]]

Now if I persist with these huge lists everywhere, it works. But it's
also completely unreadable.

So I've been trying to use types. For example

trait Problem {
type TProduct <: Product
type TOrderPart <: OrderPart[TProduct]
// etc
}

but then I eventually run into type mismatch problems as I can't seem
to figure out how to get things to work both in abstract/trait area
and when I create concrete classes. Below is a reduced example of the
code that doesn't work. It fails only on the last line - when I
actually try to do something useful. Apologies for the length of the
code, but I need enough of the bits to get to the fail point.

Thanks
Tim

object Test3 {
object BasicData {

trait BaseSite { // somewhere we go
def name: String
}
trait BaseProblem[Site <: BaseSite] {
def sites: Map[String, Site]
}
}

trait ProblemType {
import BasicData._
// with this I'm trying to eliminate huge lists of type parameters
from my types. Is there another way
type Site <: BaseSite
type Problem <: BaseProblem[Site]
}

trait ProblemGenerator extends ProblemType {
def generateProblem: Problem
}

trait Events extends ProblemType {
// Imports ProblemType so I don't need long lists of parameterised
types everywhere
trait Activity {
def finishEvent: Event
}
case class Event(
where: Site,
when: Int, // in practice will be a different
date type
next: Seq[Activity])
}

trait EventGenerator extends Events {
def generateEvents(problem: Problem) : Event // for simplicity
assume a direct graph with single root
}

object SpecificData extends ProblemType { // in practice these will
have additional data
import BasicData._
type Site = SpecificSite
type Problem = SpecificProblem

case class SpecificSite(name: String, town: String) extends
BaseSite

case class SpecificProblem(sites: Map[String, Site])
extends BaseProblem[Site]
}

class SimpleProblemGenerator extends ProblemGenerator {
import SpecificData._
type Site = SpecificSite
type Problem = SpecificProblem

def generateProblem = {
val depot = SpecificSite("depot", "London")
SpecificProblem(Map(depot.name -> depot))
}
}

class SimpleEventGenerator extends EventGenerator {

def generateEvents(problem: Problem) = {
val depot = problem.sites.get("depot").get // ok to prove point
val end = Event(depot, 12, Nil)
end
}
}

object Test {
val problemGenerator = new SimpleProblemGenerator
val eventGenerator = new SimpleEventGenerator
val events =
eventGenerator.generateEvents(problemGenerator.generateProblem) //
fails with type mismatch
}

}
------------------------
error: type mismatch;
found : Test3.SpecificData.SpecificProblem
required: Test3.Test.eventGenerator.Problem
val events =
eventGenerator.generateEvents(problemGenerator.generateProblem)

Josh Suereth

unread,
Dec 17, 2011, 8:03:16 AM12/17/11
to Tim P, scala-user

You're using path dependent types here.  You need to put all the related interworking classes into the same 'location' where the type is known to be the same.

The Problem on the generator is an overridable value, so the compiler can't assume it's OK to do that cast.

If you look up the cake pattern, it could help solve this issue.   All you utilities become nested in "package traits" that define the abstract types.

Your final software component is just an object that mixes in all the package traits and acts like a package.

Tim P

unread,
Dec 18, 2011, 4:25:07 AM12/18/11
to scala-user
Hi
In this case I'm writing an optimisation system. The EventGenerator is
a proxy for what will eventually be different optimisers (genetic
algorithms, tabu search, etc). A lot of code. It can't possibly all go
in the same "location" if that means same source file or even package.
I've tried wrapping all the other bits into a single trait, but as
long as I have concrete EventGenerators existing outside of the basic
trait and in a separate location from my definitions am I ever going
to be able to use this model? Is there any way to pass a specific
problem into an EventGenerator?
Current tidied code is at
https://github.com/TimPigden/testtypes/blob/master/src/main/scala/com/timpigden/test/Test5.scala

Tim


On Dec 17, 1:03 pm, Josh Suereth <joshua.suer...@gmail.com> wrote:
> You're using path dependent types here.  You need to put all the related
> interworking classes into the same 'location' where the type is known to be
> the same.
>
> The Problem on the generator is an overridable value, so the compiler can't
> assume it's OK to do that cast.
>
> If you look up the cake pattern, it could help solve this issue.   All you
> utilities become nested in "package traits" that define the abstract types.
>
> Your final software component is just an object that mixes in all the
> package traits and acts like a package.

Tim P

unread,
Dec 18, 2011, 6:17:28 AM12/18/11
to scala-user
Update
Miles Sabin's given me a version that compiles on

https://gist.github.com/d4968289a5aaa558a28c

Tim

Reply all
Reply to author
Forward
0 new messages