This page is no longer maintained — Please continue to the home page at www.scala-lang.org

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

3 replies
Tim P
Joined: 2011-07-28,
User offline. Last seen 1 year 4 weeks ago.

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)

Joshua.Suereth
Joined: 2008-09-02,
User offline. Last seen 32 weeks 5 days ago.
Re: trying to avoid huge lists of type parameters - is this a "

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.

On Dec 17, 2011 7:00 AM, "Tim P" <tim.pigden@optrak.com> wrote:
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)
Tim P
Joined: 2011-07-28,
User offline. Last seen 1 year 4 weeks ago.
Re: trying to avoid huge lists of type parameters - is this a "b

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/ti...

Tim
On Dec 17, 1:03 pm, Josh Suereth 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.
> On Dec 17, 2011 7:00 AM, "Tim P" wrote:
>
>
>
>
>
>
>
> > 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)

Tim P
Joined: 2011-07-28,
User offline. Last seen 1 year 4 weeks ago.
Re: trying to avoid huge lists of type parameters - is this a "b

Update
Miles Sabin's given me a version that compiles on

https://gist.github.com/d4968289a5aaa558a28c

Tim

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland