title: Flattening nested lists of the same type
body: <p>Let's say I want to flatten nested lists of the same type... For example</p>
  
  <pre><code>    ListA(Element(A), Element(B), ListA(Element(C), Element(D)), ListB(Element(E),Element(F)))
  </code></pre>
  
  <p><code>ListA</code> contains nested list of the same type (<code>ListA(Element(C), Element(D))</code>) so I want to substitute it with the values it contains, so the result of the upper example should look like this:</p>
  
  <pre><code>ListA(Element(A), Element(B), Element(C), Element(D), ListB(Element(E),Element(F)))
  </code></pre>
  
  <p>Current class hierarchy:</p>
  
  <pre><code>abstract class SpecialList() extends Exp {
      val elements: List[Exp]
  }
  
  case class Element(name: String) extends Exp
  
  case class ListA(elements: List[Exp]) extends SpecialList {
          override def toString(): String = "ListA("+elements.mkString(",")+")"
  }
  
  case class ListB(elements: List[Exp]) extends SpecialList {
          override def toString(): String = "ListB("+elements.mkString(",")+")"
  }
  
  object ListA{def apply(elements: Exp*):ListA = ListA(elements.toList)}
  object ListB{def apply(elements: Exp*):ListB = ListB(elements.toList)}
  </code></pre>
  
  <p>I have made three solutions that works, but I think there have to be better way to achieve this:</p>
  
  <p><strong>First solution:</strong></p>
  
  <pre><code>def flatten[T &lt;: SpecialList](parentList: T): List[Exp] = {
          val buf = new ListBuffer[Exp]
  
          for (feature &lt;- parentList.elements) feature match {
              case listA:ListA if parentList.isInstanceOf[ListA] =&gt; buf ++= listA.elements
              case listB:ListB if parentList.isInstanceOf[ListB] =&gt; buf ++= listB.elements
              case _ =&gt; buf += feature
          }
          buf.toList
      }
  </code></pre>
  
  <p><strong>Second solution:</strong></p>
  
  <pre><code>def flatten[T &lt;: SpecialList](parentList: T): List[Exp] = {
      val buf = new ListBuffer[Exp]
  
      parentList match {
          case listA:ListA =&gt; for (elem &lt;- listA.elements) elem match {
                                  case listOfTypeA:ListA =&gt; buf ++= listOfTypeA.elements
                                  case _ =&gt; buf += elem
                              }
  
          case listB:ListB =&gt; for (elem &lt;- listB.elements) elem match {
                                  case listOfTypeB:ListB =&gt; buf ++= listOfTypeB.elements
                                  case _ =&gt; buf += elem
                              }
      }
  
      buf.toList
  }
  </code></pre>
  
  <p><strong>Third solution</strong></p>
  
  <pre><code>def flatten[T &lt;: SpecialList](parentList: T): List[Exp] = parentList.elements flatMap {
      case listA:ListA if parentList.isInstanceOf[ListA] =&gt; listA.elements
      case listB:ListB if parentList.isInstanceOf[ListB] =&gt; listB.elements
      case other =&gt; List(other)
  }
  </code></pre>
  
  <p>My question is whether there is any better, more generic way to achieve same functionality as in all of upper three solutions there is repetition of code? </p>