Let's say we have the following types:
sealed trait T
case object Goat extends T
case object Monk extends T
case object Tiger extends T
Now, how do you construct a collection of T
such that at least one of each sub-T
appears in the collection, this constraint being enforced at compile time? A collection where, contrary to this:
val s:Seq[T] = Seq(Goat)
which compiles, this:
val ts:TotalSeq[T] = TotalSeq(Goat, Goat, Tiger)
does not. I've used Scala above, but I would be happy to see solutions in other languages as well.
(Forgive me if my words are a bit off; I have a cold and am amusing myself today with puzzles.)
It looks like the builder pattern with generalized type constraints: http://www.tikalk.com/java/blog/type-safe-builder-scala-using-type-constraints
Something like:
sealed trait TBoolean
sealed trait TTrue extends TBoolean
sealed trait TFalse extends TBoolean
class SeqBuilder[HasGoat <: TBoolean, HasMonk <: TBoolean, HasTiger <: TBoolean] private (seq: Seq[T]) {
def +=(g: Goat.type) = new SeqBuilder[TTrue, HasMonk, HasTiger](g +: seq)
def +=(m: Monk.type) = new SeqBuilder[HasGoat, TTrue, HasTiger](m +: seq)
def +=(t: Tiger.type) = new SeqBuilder[HasGoat, HasMonk, TTrue](t +: seq)
def build()(implicit evg: HasGoat =:= TTrue, evm: HasMonk =:= TTrue, evt: HasTiger =:= TTrue) = seq
}
object SeqBuilder {
def apply = new SeqBuilder(Seq())
}
Usage:
scala> SeqBuilder() + Goat + Tiger + Monk build()
res21: Seq[T] = List(Monk, Tiger, Goat)
scala> SeqBuilder() + Goat + Goat + Monk build()
<console>:34: error: Cannot prove that Nothing =:= TTrue.
SeqBuilder() + Goat + Goat + Monk build()
^
If the number of instances you want grows you can try using an HMap
Other similar approaches: phantom types: http://james-iry.blogspot.com/2010/10/phantom-types-in-haskell-and-scala.html
Another approach is to ask why you need all 3 objects. Say you want to carry an operation when the seq has all three, then you can do something like:
object SeqBuilder {
val all = Seq(Goat, Monk, Tiger)
def apply(t: T*)(onAll: Seq[T] => Unit)(onViolation: => Unit) = {
val seq = Seq(t:_*)
if(all forall seq.contains) onAll(seq)
else onViolation
}
}
That way, the function onAll is not called if not all required Ts have been supplied and otherwise the violation function is called
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With