Skip to content

Instantly share code, notes, and snippets.

@manjuraj
Last active August 29, 2015 13:57
Show Gist options
  • Save manjuraj/9492759 to your computer and use it in GitHub Desktop.
Save manjuraj/9492759 to your computer and use it in GitHub Desktop.
Clever use of typeclasses to enrich existing type while preserving existing type in type system
// Type information got lost because of implicit conversion - instead of type
// Array[T], we have the runtime type WrappedArray[T]
scala> def first[T](x: Traversable[T]) = (x.head, x)
first: [T](x: Traversable[T])(T, Traversable[T])
scala> first(Array(1,2,3))
res0: (Int, Traversable[Int]) = (1,WrappedArray(1, 2, 3))
// Use implicit type constraints when we want to enrich an existing type while
// preserving that type in the type system
scala> :paste
// Entering paste mode (ctrl-D to finish)
trait ExtendedOps[F[_]] {
def first[A](c: F[A]): (A, F[A])
}
object ExtendedOps {
implicit object ArrayOps extends ExtendedOps[Array] {
def first[A](c: Array[A]) = (c.head, c.tail)
}
// functional extension method
def first[A, F[_]](c: F[A])(implicit ev: ExtendedOps[F]) = ev.first(c)
}
import ExtendedOps._
// Exiting paste mode, now interpreting.
warning: there were 2 feature warning(s); re-run with -feature for details
defined trait ExtendedOps
defined module ExtendedOps
import ExtendedOps._
scala> first(Array(1,2,3))
res2: (Int, Array[Int]) = (1,Array(2, 3))
scala> :paste
// Entering paste mode (ctrl-D to finish)
// object-oriented extension method - pimp an existing type Array[A]
implicit class ArrayAugs[A](val x: Array[A]) extends AnyVal {
def first(implicit ev: ExtendedOps[Array]) = ev.first(x)
}
// Exiting paste mode, now interpreting.
defined class ArrayAugs
scala> Array(1,2,3).first
res0: (Int, Array[Int]) = (1,Array(2, 3))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment