I have been writing some code and it has lead me to:
trait SplitStrategy[E] {
def apply(seq: Seq[E]): Seq[(Int, Seq[E])]
}
object SplitByConsecutiveElements {
def apply[E](consecutiveValue: Seq[E] => E): SplitByConsecutiveElements[E] = new SplitByConsecutiveElements(consecutiveValue)
def withConsecutiveValueAsTheHighestCount[E]: SplitByConsecutiveElements[E] = {
val consecutiveValue: Seq[E] => E = seq => {
seq.foldLeft(Map.empty[E, Int].withDefaultValue(0)) {
case (m, v) => m.updated(v, m(v) + 1)
}.maxBy(_._2)._1
}
SplitByConsecutiveElements(consecutiveValue)
}
def main(args: Array[String]): Unit = {
println(SplitByConsecutiveElements.withConsecutiveValueAsTheHighestCount.apply(Seq(1, 1, 2, 1, 2)))
}
}
class SplitByConsecutiveElements[E](val consecutiveValue: Seq[E] => E) extends SplitStrategy[E] {
override def apply(seq: Seq[E]): Seq[(Int, Seq[E])] = splitSequenceBySequenceOfElements(seq, consecutiveValue(seq))
private def splitSequenceBySequenceOfElements[E](seq: Seq[E], consecutiveValue: E): Seq[(Int, Seq[E])] = {
// This is just a dummy operation, not the real algorithm
Seq((0, seq.filter(consecutiveValue == _)))
}
}
If you look at the "main" method, you can see I call SplitByConsecutiveElements.withConsecutiveValueAsTheHighestCount
and then I apply it to a Sequence of Int
. At first, I though it would not compile. But it does compile and works properly.
My rationale for it to not work is that when calling SplitByConsecutiveElements.withConsecutiveValueAsTheHighestCount
I am creating a SplitByConsecutiveElements[E]
of unknown type parameter E
. Then I apply SplitByConsecutiveElements[E].someMethod
, where E
is known, but I have already created an instance of SplitByConsecutiveElements[E]
and not of SplitByConsecutiveElements[Int]
, for example. Why does this work? It is like the class transofrms itself at compile time?
My head is just a mess right now, I hope I conveyed my concern well.
There are two reasons why it works:
1) Generics on JVM are erased so it's possible to create an instance of SplitStrategy[E]
without knowing E
. So no need to "transform the class at compile time".
2) Even if we were on a platform where generic types are not erased (say, CLR for C#) this code still could work.
Compiler tries to infer E
in SplitStrategy[E]
based on the context to make the entire expression valid. In you case it successfully guesses that E
is Int
.