Perhaps the most idiomatic way would be to use the so called Pimp My Library Pattern, which allows one to add extension methods to any classes via implicit class conversions.
For example, to add a consolidate
method to List
s, you could write:
object ListExtensions {
implicit class ConsolidatableList[T](val lst: List[T]) extends AnyVal {
def consolidate = ??? // Do whatever you want with the List lst
}
}
import ListExtensions._
List(1,2,3)
.map(x => x * x)
.consolidate
If you really want to be able to call arbitrary functions on arbitrary objects in a chain, you could even define an "pipeline" operator like in F#:
object Extensions {
implicit class PipelineExtension[T](val obj: T) extends AnyVal {
def |>[S](f: T => S): S = f(obj)
}
}
import Extensions._
List(1,2,3)
.map(x => x * x)
.|> (consolidate)
Query( ... )
.map { ... }
.filter { ... }
.combinations(2)
.|> { col =>
...
consolidate(col)
}
The |>
operator behaves like the pipeTo
method you have defined.
Basically, this works by creating an implicit conversion from your type to the type with the various extension methods that you define. The AnyVal
makes the extension class an value class, so its use will have little to no runtime cost.