import java.util.Date sealed trait DataFrame[A] { import DataFrame._ def size: Int = this.foldLeft(0)( (s, _) => s + 1 ) def foldLeft[B](z: B)(f: (B, A) => B): B = this match { case Apply(data) => data.foldLeft(z)(f) case Filter(s, filter) => s.foldLeft(z){ (b, a) => if(filter(a)) f(b, a) else b } case Project(s, project) => s.foldLeft(z){ (b, a) => f(b, project(a)) } } def filter(f: A => Boolean): DataFrame[A] = Filter(this, f) def project[B](f: A => B): DataFrame[B] = Project(this, f) } object DataFrame { // Allows us to write DataFrame(1, 2, 3, 4) def apply[A](as: A*): DataFrame[A] = Apply(as.toList) def apply[A](data: List[A]): DataFrame[A] = Apply(data) // DataFrame algebraic data type final case class Apply[A](data: List[A]) extends DataFrame[A] final case class Filter[A](source: DataFrame[A], f: A => Boolean) extends DataFrame[A] final case class Project[A,B](source: DataFrame[A], f: A => B) extends DataFrame[B] } object DataFrameSyntax { implicit class DoubleSyntax(data: DataFrame[Double]) { def sum: Double = data.foldLeft(0.0)(_ + _) def average: Double = data.sum / data.size } }