Как я могу разделить список ADT на его варианты? - PullRequest
0 голосов
/ 28 ноября 2018

Можно ли как-нибудь расширить решение до типа суммы?

sealed trait Group
case class A extends Group
case class B extends Group
case class C extends Group
def divide(l : List[Group]): //Something from what I can extract List[A], List[B] and List[C]

Ответы [ 2 ]

0 голосов
/ 01 декабря 2018

Сделал это сам, используя Shapeless:

import shapeless.{:+:, ::, CNil, Coproduct, Generic, HList, HNil}

/*
Suppose we have a sealed trait and few implementations:
sealed trait Animal
case class Cat(a: Int) extends Animal
case class Dog(b: Int) extends Animal
case class Fox(c: Int) extends Animal

and a list:
val animals: List[Animal]

how to split the list into sub-lists per a subclass?
val cats: List[Cat] = ???
val dogs: List[Dog] = ???
val foxes: List[Fox] = ???

Of course it must work w/o boilerplate for arbitrary numbers of children
 */
object Split {

  trait Splitter[T <: Coproduct] {
    type R <: HList

    def split(list: List[T]): R

  }

  type Aux[T <: Coproduct, R0 <: HList] = Splitter[T] {
    type R = R0
  }

  implicit val cNilSplitter = new Splitter[CNil] {
    type R = HNil

    override def split(list: List[CNil]): HNil = HNil
  }

  implicit def cPllusSplitter[H, T <: Coproduct, R <: HList](implicit ev: Aux[T, R]): Aux[H :+: T, List[H] :: ev.R] = new Splitter[H :+: T] {
    type R = List[H] :: ev.R

    override def split(list: List[H :+: T]): ::[List[H], ev.R] = {
      val heads: List[H] = list.flatMap(e => e.eliminate(h => Some(h), t => None))
      val tails: List[T] = list.flatMap(e => e.eliminate(h => None, t => Some(t)))
      val sub: ev.R = ev.split(tails)
      heads :: sub
    }
  }

  def splitCoproduct[T <: Coproduct, R <: HList](list: List[T])(implicit ev: Aux[T, R]): R = ev.split(list)

  def split[X, T <: Coproduct, R <: HList](list: List[X])(implicit gen: Generic.Aux[X, T], ev: Aux[T, R]): R = {
    val asCoproduct: List[T] = list.map(gen.to)
    splitCoproduct[T, R](asCoproduct)(ev)
  }

}


object Runner {

  import Split._

  def main(args: Array[String]): Unit = {

    sealed trait Animal
    case class Cat(a: Int) extends Animal
    case class Dog(b: Int) extends Animal
    case class Fox(c: Int) extends Animal

    val animals: List[Animal] = List(Cat(1), Dog(1), Cat(2), Fox(1), Dog(2), Dog(3))
    val result = split(animals) //List[Cat] :: List[Dog] :: List[Fox] :: HNil
    println(result)
    val cats: List[Cat] = result.head
    val dogs: List[Dog] = result.tail.head
    val foxes: List[Fox] = result.tail.tail.head
    println(cats)
    println(dogs)
    println(foxes)
  }
}
0 голосов
/ 29 ноября 2018

Может быть, вы можете попытаться улучшить этот ответ.Это может не решить вашу проблему, так как трудно узнать произвольные подтипы данного типа (тип Group может иметь любое количество подтипов).В случае Either его подтип легко предсказать как Right или Left.

sealed trait Group
case class A(name:String) extends Group
case class B(name:String) extends Group
case class C(name:String) extends Group

val list = List(
                A("a1"), A("a2"), A("a3"), A("a4"), 
                B("b1"), B("b2"), B("b3"), B("b4"), 
                C("c1"), C("c2"), C("c3"), C("c4")
                )  

def divide(
   list: List[Group],
   aList : List[A], 
   bList: List[B], 
   cList: List[C]
): (List[A], List[B], List[C]) = {
  list match {
    case Nil => (aList, bList, cList)
    case head :: tail => head match {
      case a : A => divide(tail, aList.:+(a), bList, cList)
      case b : B =>  divide(tail,aList, bList.:+(b), cList)
      case c : C => divide(tail, aList, bList, cList.:+(c))
    }
  }
}

divide(list, List.empty[A], List.empty[B], List.empty[C])
//res1: (List[A], List[B], List[C]) = (List(A(a1), A(a2), A(a3), A(a4)),List(B(b1), B(b2), B(b3), B(b4)),List(C(c1), C(c2), C(c3), C(c4)))

Надеюсь, это поможет вам.

...