In Scala, how to deal with heterogeneous list of the same parameterized type












0















I have an array of Any (in real life, it's a Spark Row, but it's sufficient to isolate the problem)



object Row {
val buffer : Array[Any] = Array(42, 21, true)
}


And I want to apply some operations on its elements.
So, I've defined a simple ADT to define a compute operation on a type A



  trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}

case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}


Given that I have a list of all operations and I know which operation is to apply to each element, let's use these operations.



object GenericsOp {
import Row._

val ops = Seq(Count, Exist)

def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}


By design, for a given op, types are aligned between cast and combine. But unfortunately the following code does not compile. The error is



Type mismatch, expected: _$1, actual: AnyVal


Is there a way to make it work ?



I've found a workaround by using abstract type member instead of type parameter.



object AbstractOp extends App {
import Row._

trait Op {
type A
def compute(a: A) : A
}

case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}

val ops = Seq(Count, Exist)

def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}


Is there a better way ?










share|improve this question




















  • 2





    I'm not sure what you attemted to achieve there, but are you aware of Shapeless' HLists?

    – Andrey Tyukin
    Jan 2 at 21:15











  • Hi @AndreyTyukin. I've reworded the question to make it clearer what I'm trying to achieve. I'm aware of Shapeless but I'd like to understand what is possible with raw Scala.

    – Yann Moisan
    Jan 3 at 13:10
















0















I have an array of Any (in real life, it's a Spark Row, but it's sufficient to isolate the problem)



object Row {
val buffer : Array[Any] = Array(42, 21, true)
}


And I want to apply some operations on its elements.
So, I've defined a simple ADT to define a compute operation on a type A



  trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}

case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}


Given that I have a list of all operations and I know which operation is to apply to each element, let's use these operations.



object GenericsOp {
import Row._

val ops = Seq(Count, Exist)

def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}


By design, for a given op, types are aligned between cast and combine. But unfortunately the following code does not compile. The error is



Type mismatch, expected: _$1, actual: AnyVal


Is there a way to make it work ?



I've found a workaround by using abstract type member instead of type parameter.



object AbstractOp extends App {
import Row._

trait Op {
type A
def compute(a: A) : A
}

case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}

val ops = Seq(Count, Exist)

def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}


Is there a better way ?










share|improve this question




















  • 2





    I'm not sure what you attemted to achieve there, but are you aware of Shapeless' HLists?

    – Andrey Tyukin
    Jan 2 at 21:15











  • Hi @AndreyTyukin. I've reworded the question to make it clearer what I'm trying to achieve. I'm aware of Shapeless but I'd like to understand what is possible with raw Scala.

    – Yann Moisan
    Jan 3 at 13:10














0












0








0








I have an array of Any (in real life, it's a Spark Row, but it's sufficient to isolate the problem)



object Row {
val buffer : Array[Any] = Array(42, 21, true)
}


And I want to apply some operations on its elements.
So, I've defined a simple ADT to define a compute operation on a type A



  trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}

case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}


Given that I have a list of all operations and I know which operation is to apply to each element, let's use these operations.



object GenericsOp {
import Row._

val ops = Seq(Count, Exist)

def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}


By design, for a given op, types are aligned between cast and combine. But unfortunately the following code does not compile. The error is



Type mismatch, expected: _$1, actual: AnyVal


Is there a way to make it work ?



I've found a workaround by using abstract type member instead of type parameter.



object AbstractOp extends App {
import Row._

trait Op {
type A
def compute(a: A) : A
}

case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}

val ops = Seq(Count, Exist)

def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}


Is there a better way ?










share|improve this question
















I have an array of Any (in real life, it's a Spark Row, but it's sufficient to isolate the problem)



object Row {
val buffer : Array[Any] = Array(42, 21, true)
}


And I want to apply some operations on its elements.
So, I've defined a simple ADT to define a compute operation on a type A



  trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}

case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}


Given that I have a list of all operations and I know which operation is to apply to each element, let's use these operations.



object GenericsOp {
import Row._

val ops = Seq(Count, Exist)

def compute() = {
buffer(0) = ops(0).compute(ops(0).cast(buffer(0)))
buffer(1) = ops(0).compute(ops(0).cast(buffer(1)))
buffer(2) = ops(1).compute(ops(1).cast(buffer(2)))
}
}


By design, for a given op, types are aligned between cast and combine. But unfortunately the following code does not compile. The error is



Type mismatch, expected: _$1, actual: AnyVal


Is there a way to make it work ?



I've found a workaround by using abstract type member instead of type parameter.



object AbstractOp extends App {
import Row._

trait Op {
type A
def compute(a: A) : A
}

case object Count extends Op {
type A = Int
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op {
type A = Boolean
override def compute(a: Boolean): Boolean = a
}

val ops = Seq(Count, Exist)

def compute() = {
val op0 = ops(0)
val op1 = ops(1)
buffer(0) = ops(0).compute(buffer(0).asInstanceOf[op0.A])
buffer(1) = ops(0).compute(buffer(1).asInstanceOf[op0.A])
buffer(2) = ops(1).compute(buffer(2).asInstanceOf[op1.A])
}
}


Is there a better way ?







scala






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 3 at 13:08







Yann Moisan

















asked Jan 2 at 21:02









Yann MoisanYann Moisan

4,31232156




4,31232156








  • 2





    I'm not sure what you attemted to achieve there, but are you aware of Shapeless' HLists?

    – Andrey Tyukin
    Jan 2 at 21:15











  • Hi @AndreyTyukin. I've reworded the question to make it clearer what I'm trying to achieve. I'm aware of Shapeless but I'd like to understand what is possible with raw Scala.

    – Yann Moisan
    Jan 3 at 13:10














  • 2





    I'm not sure what you attemted to achieve there, but are you aware of Shapeless' HLists?

    – Andrey Tyukin
    Jan 2 at 21:15











  • Hi @AndreyTyukin. I've reworded the question to make it clearer what I'm trying to achieve. I'm aware of Shapeless but I'd like to understand what is possible with raw Scala.

    – Yann Moisan
    Jan 3 at 13:10








2




2





I'm not sure what you attemted to achieve there, but are you aware of Shapeless' HLists?

– Andrey Tyukin
Jan 2 at 21:15





I'm not sure what you attemted to achieve there, but are you aware of Shapeless' HLists?

– Andrey Tyukin
Jan 2 at 21:15













Hi @AndreyTyukin. I've reworded the question to make it clearer what I'm trying to achieve. I'm aware of Shapeless but I'd like to understand what is possible with raw Scala.

– Yann Moisan
Jan 3 at 13:10





Hi @AndreyTyukin. I've reworded the question to make it clearer what I'm trying to achieve. I'm aware of Shapeless but I'd like to understand what is possible with raw Scala.

– Yann Moisan
Jan 3 at 13:10












2 Answers
2






active

oldest

votes


















2














It seems that your code can be simplified by making Op[A] extend Any => A:



trait Op[A] extends (Any => A) {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
def apply(a: Any): A = compute(cast(a))
}

case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}

object AbstractOp {

val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)

def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i)(buffer(i))
}
println(buffer.mkString("[", ",", "]"))
}
}


Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.





Update



If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:



trait Op[A] {
def cast(a: Any) : A = a.asInstanceOf[A]
def compute(a: A) : A
}

case object Count extends Op[Int] {
override def compute(a: Int): Int = a + 1
}

case object Exist extends Op[Boolean] {
override def compute(a: Boolean): Boolean = a
}

object AbstractOp {

val buffer: Array[Any] = Array(42, 21, true)
val ops: Array[Op[_]] = Array(Count, Count, Exist)

def main(args: Array[String]): Unit = {
for (i <- 0 until buffer.size) {
buffer(i) = ops(i) match {
case op: Op[t] => op.compute(op.cast(buffer(i)))
}
}
println(buffer.mkString("[", ",", "]"))
}
}


Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.






share|improve this answer

































    1














    As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:



    def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))

    buffer(0) = apply(ops(0), buffer(0))





    share|improve this answer
























    • Actually, you don't really need any additional methods at all. See update.

      – Andrey Tyukin
      Jan 5 at 3:45











    • Yes, that's another option.

      – Alexey Romanov
      Jan 8 at 17:34












    Your Answer






    StackExchange.ifUsing("editor", function () {
    StackExchange.using("externalEditor", function () {
    StackExchange.using("snippets", function () {
    StackExchange.snippets.init();
    });
    });
    }, "code-snippets");

    StackExchange.ready(function() {
    var channelOptions = {
    tags: "".split(" "),
    id: "1"
    };
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function() {
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled) {
    StackExchange.using("snippets", function() {
    createEditor();
    });
    }
    else {
    createEditor();
    }
    });

    function createEditor() {
    StackExchange.prepareEditor({
    heartbeatType: 'answer',
    autoActivateHeartbeat: false,
    convertImagesToLinks: true,
    noModals: true,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    imageUploader: {
    brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
    contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
    allowUrls: true
    },
    onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    });


    }
    });














    draft saved

    draft discarded


















    StackExchange.ready(
    function () {
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54013149%2fin-scala-how-to-deal-with-heterogeneous-list-of-the-same-parameterized-type%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    2 Answers
    2






    active

    oldest

    votes








    2 Answers
    2






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    2














    It seems that your code can be simplified by making Op[A] extend Any => A:



    trait Op[A] extends (Any => A) {
    def cast(a: Any) : A = a.asInstanceOf[A]
    def compute(a: A) : A
    def apply(a: Any): A = compute(cast(a))
    }

    case object Count extends Op[Int] {
    override def compute(a: Int): Int = a + 1
    }

    case object Exist extends Op[Boolean] {
    override def compute(a: Boolean): Boolean = a
    }

    object AbstractOp {

    val buffer: Array[Any] = Array(42, 21, true)
    val ops: Array[Op[_]] = Array(Count, Count, Exist)

    def main(args: Array[String]): Unit = {
    for (i <- 0 until buffer.size) {
    buffer(i) = ops(i)(buffer(i))
    }
    println(buffer.mkString("[", ",", "]"))
    }
    }


    Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.





    Update



    If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:



    trait Op[A] {
    def cast(a: Any) : A = a.asInstanceOf[A]
    def compute(a: A) : A
    }

    case object Count extends Op[Int] {
    override def compute(a: Int): Int = a + 1
    }

    case object Exist extends Op[Boolean] {
    override def compute(a: Boolean): Boolean = a
    }

    object AbstractOp {

    val buffer: Array[Any] = Array(42, 21, true)
    val ops: Array[Op[_]] = Array(Count, Count, Exist)

    def main(args: Array[String]): Unit = {
    for (i <- 0 until buffer.size) {
    buffer(i) = ops(i) match {
    case op: Op[t] => op.compute(op.cast(buffer(i)))
    }
    }
    println(buffer.mkString("[", ",", "]"))
    }
    }


    Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.






    share|improve this answer






























      2














      It seems that your code can be simplified by making Op[A] extend Any => A:



      trait Op[A] extends (Any => A) {
      def cast(a: Any) : A = a.asInstanceOf[A]
      def compute(a: A) : A
      def apply(a: Any): A = compute(cast(a))
      }

      case object Count extends Op[Int] {
      override def compute(a: Int): Int = a + 1
      }

      case object Exist extends Op[Boolean] {
      override def compute(a: Boolean): Boolean = a
      }

      object AbstractOp {

      val buffer: Array[Any] = Array(42, 21, true)
      val ops: Array[Op[_]] = Array(Count, Count, Exist)

      def main(args: Array[String]): Unit = {
      for (i <- 0 until buffer.size) {
      buffer(i) = ops(i)(buffer(i))
      }
      println(buffer.mkString("[", ",", "]"))
      }
      }


      Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.





      Update



      If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:



      trait Op[A] {
      def cast(a: Any) : A = a.asInstanceOf[A]
      def compute(a: A) : A
      }

      case object Count extends Op[Int] {
      override def compute(a: Int): Int = a + 1
      }

      case object Exist extends Op[Boolean] {
      override def compute(a: Boolean): Boolean = a
      }

      object AbstractOp {

      val buffer: Array[Any] = Array(42, 21, true)
      val ops: Array[Op[_]] = Array(Count, Count, Exist)

      def main(args: Array[String]): Unit = {
      for (i <- 0 until buffer.size) {
      buffer(i) = ops(i) match {
      case op: Op[t] => op.compute(op.cast(buffer(i)))
      }
      }
      println(buffer.mkString("[", ",", "]"))
      }
      }


      Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.






      share|improve this answer




























        2












        2








        2







        It seems that your code can be simplified by making Op[A] extend Any => A:



        trait Op[A] extends (Any => A) {
        def cast(a: Any) : A = a.asInstanceOf[A]
        def compute(a: A) : A
        def apply(a: Any): A = compute(cast(a))
        }

        case object Count extends Op[Int] {
        override def compute(a: Int): Int = a + 1
        }

        case object Exist extends Op[Boolean] {
        override def compute(a: Boolean): Boolean = a
        }

        object AbstractOp {

        val buffer: Array[Any] = Array(42, 21, true)
        val ops: Array[Op[_]] = Array(Count, Count, Exist)

        def main(args: Array[String]): Unit = {
        for (i <- 0 until buffer.size) {
        buffer(i) = ops(i)(buffer(i))
        }
        println(buffer.mkString("[", ",", "]"))
        }
        }


        Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.





        Update



        If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:



        trait Op[A] {
        def cast(a: Any) : A = a.asInstanceOf[A]
        def compute(a: A) : A
        }

        case object Count extends Op[Int] {
        override def compute(a: Int): Int = a + 1
        }

        case object Exist extends Op[Boolean] {
        override def compute(a: Boolean): Boolean = a
        }

        object AbstractOp {

        val buffer: Array[Any] = Array(42, 21, true)
        val ops: Array[Op[_]] = Array(Count, Count, Exist)

        def main(args: Array[String]): Unit = {
        for (i <- 0 until buffer.size) {
        buffer(i) = ops(i) match {
        case op: Op[t] => op.compute(op.cast(buffer(i)))
        }
        }
        println(buffer.mkString("[", ",", "]"))
        }
        }


        Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.






        share|improve this answer















        It seems that your code can be simplified by making Op[A] extend Any => A:



        trait Op[A] extends (Any => A) {
        def cast(a: Any) : A = a.asInstanceOf[A]
        def compute(a: A) : A
        def apply(a: Any): A = compute(cast(a))
        }

        case object Count extends Op[Int] {
        override def compute(a: Int): Int = a + 1
        }

        case object Exist extends Op[Boolean] {
        override def compute(a: Boolean): Boolean = a
        }

        object AbstractOp {

        val buffer: Array[Any] = Array(42, 21, true)
        val ops: Array[Op[_]] = Array(Count, Count, Exist)

        def main(args: Array[String]): Unit = {
        for (i <- 0 until buffer.size) {
        buffer(i) = ops(i)(buffer(i))
        }
        println(buffer.mkString("[", ",", "]"))
        }
        }


        Since it's asInstanceOf everywhere anyway, it does not make the code any less safe than what you had previously.





        Update



        If you cannot change the Op interface, then invoking cast and compute is a bit more cumbersome, but still possible:



        trait Op[A] {
        def cast(a: Any) : A = a.asInstanceOf[A]
        def compute(a: A) : A
        }

        case object Count extends Op[Int] {
        override def compute(a: Int): Int = a + 1
        }

        case object Exist extends Op[Boolean] {
        override def compute(a: Boolean): Boolean = a
        }

        object AbstractOp {

        val buffer: Array[Any] = Array(42, 21, true)
        val ops: Array[Op[_]] = Array(Count, Count, Exist)

        def main(args: Array[String]): Unit = {
        for (i <- 0 until buffer.size) {
        buffer(i) = ops(i) match {
        case op: Op[t] => op.compute(op.cast(buffer(i)))
        }
        }
        println(buffer.mkString("[", ",", "]"))
        }
        }


        Note the ops(i) match { case op: Opt[t] => ... } part with a type-parameter in the pattern: this allows us to make sure that cast returns a t that is accepted by compute.







        share|improve this answer














        share|improve this answer



        share|improve this answer








        edited Jan 5 at 2:32

























        answered Jan 3 at 13:32









        Andrey TyukinAndrey Tyukin

        30.2k42351




        30.2k42351

























            1














            As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:



            def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))

            buffer(0) = apply(ops(0), buffer(0))





            share|improve this answer
























            • Actually, you don't really need any additional methods at all. See update.

              – Andrey Tyukin
              Jan 5 at 3:45











            • Yes, that's another option.

              – Alexey Romanov
              Jan 8 at 17:34
















            1














            As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:



            def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))

            buffer(0) = apply(ops(0), buffer(0))





            share|improve this answer
























            • Actually, you don't really need any additional methods at all. See update.

              – Andrey Tyukin
              Jan 5 at 3:45











            • Yes, that's another option.

              – Alexey Romanov
              Jan 8 at 17:34














            1












            1








            1







            As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:



            def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))

            buffer(0) = apply(ops(0), buffer(0))





            share|improve this answer













            As a more general solution than Andrey Tyukin's, you can define the method outside Op, so it works even if Op can't be modified:



            def apply[A](op: Op[A], x: Any) = op.compute(op.cast(x))

            buffer(0) = apply(ops(0), buffer(0))






            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Jan 4 at 19:56









            Alexey RomanovAlexey Romanov

            111k26215358




            111k26215358













            • Actually, you don't really need any additional methods at all. See update.

              – Andrey Tyukin
              Jan 5 at 3:45











            • Yes, that's another option.

              – Alexey Romanov
              Jan 8 at 17:34



















            • Actually, you don't really need any additional methods at all. See update.

              – Andrey Tyukin
              Jan 5 at 3:45











            • Yes, that's another option.

              – Alexey Romanov
              Jan 8 at 17:34

















            Actually, you don't really need any additional methods at all. See update.

            – Andrey Tyukin
            Jan 5 at 3:45





            Actually, you don't really need any additional methods at all. See update.

            – Andrey Tyukin
            Jan 5 at 3:45













            Yes, that's another option.

            – Alexey Romanov
            Jan 8 at 17:34





            Yes, that's another option.

            – Alexey Romanov
            Jan 8 at 17:34


















            draft saved

            draft discarded




















































            Thanks for contributing an answer to Stack Overflow!


            • Please be sure to answer the question. Provide details and share your research!

            But avoid



            • Asking for help, clarification, or responding to other answers.

            • Making statements based on opinion; back them up with references or personal experience.


            To learn more, see our tips on writing great answers.




            draft saved


            draft discarded














            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54013149%2fin-scala-how-to-deal-with-heterogeneous-list-of-the-same-parameterized-type%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown





















































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown

































            Required, but never shown














            Required, but never shown












            Required, but never shown







            Required, but never shown







            Popular posts from this blog

            MongoDB - Not Authorized To Execute Command

            How to fix TextFormField cause rebuild widget in Flutter

            in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith