Skip to content

Instantly share code, notes, and snippets.

@mikea
Last active December 27, 2015 03:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikea/7259269 to your computer and use it in GitHub Desktop.
Save mikea/7259269 to your computer and use it in GitHub Desktop.
I want C[T] to have different method sets based on T. I thought I could use implicits, but can't make it work. Any idea how to fix this example?
class Printer[T](_t: T) {
def t = _t
def print():Unit = { println(t) }
}
class IterablePrinter[T, I <: Iterable[T]](p : Printer[I]) {
def printEach() = p.t.foreach(println)
}
object Test extends App {
new Printer(100).print()
new Printer(List(1, 2, 3)).print()
new IterablePrinter[Int, List[Int]](new Printer(List(1, 2, 3))).printEach()
// Define implicits to make this work:
new Printer(List(1, 2, 3)).printEach()
}
@izmailoff
Copy link

import language._

class Printer[T](_t: T) {
  def t = _t
  def print(): Unit = println(t) //or: just println(_t) without declaring t
}

class IterablePrinter[T](p : Printer[_ <: Iterable[T]]) {
  def printEach(): Unit = {
    for (v <- p.t) {
      println(v)
    }
  }
  //or:  def printEach(): Unit = p.t.foreach(println)
}

object Test extends App {
  new Printer(100).print()
  new Printer(List(1, 2, 3)).print()
  new IterablePrinter(new Printer(List(1, 2, 3))).printEach()

  // Define implicits to make this work:
  implicit def printer2iterablePrinter[T](p: Printer[_ <: Iterable[T]]): IterablePrinter[T] = new IterablePrinter(p)

  // construct is possible:
  new IterablePrinter(new Printer(List(1, 2, 3))).printEach()

  // function applied regularly:
  printer2iterablePrinter(new Printer(List(1, 2, 3))).printEach()

  // implicit works:  
  new Printer(List(1, 2, 3)).printEach()
}

@izmailoff
Copy link

compile with -feature flag, for instance: scalac -feature implicits.scala.

@ConnorDoyle
Copy link

Idiomatically, the implicit conversion might reside within object Printer { object Implicits { /* ... */ }} so you can bring them into scope with import Printer.Implicits._.

It's sufficient to import scala.languageFeature.implicitConversions, no need for the compiler flag.

@vigdorchik
Copy link

trait Printable {
  def print(): Unit
}

object Implicits {
  implicit class AnyRefPrinter(obj: AnyRef) extends Printable {
    def print() = Console.println(obj.toString)
  }

  implicit class IterablePrinter[T <% Printable](obj: Iterable[T]) extends Printable {
    def print() = obj foreach (_.print())
  }
}

import Implicits._

"Print me".print()
List("Print me too").print()

@vigdorchik
Copy link

bash-4.1$ scala printer.scala
Print me
Print me too

@sergey-scherbina
Copy link

object TestApp extends App {

  trait Printer[-T] {
    def print(p: T)
  }

  implicit class PrinterOp[T: Printer](val p: T) {
    def print = implicitly[Printer[T]].print(p)
  }

  trait LowPriorityPrinter {

    implicit object StdPrinter extends Printer[Any] {
      def print(p: Any) = println(p)
    }

  }

  object Printer extends LowPriorityPrinter {

    implicit object IterPrinter extends Printer[Iterable[_]] {
      def print(p: Iterable[_]) = p.foreach(_.print)
    }

  }

  "hello".print

  List("hello", "world").print

}

@mikea
Copy link
Author

mikea commented Nov 1, 2013

Guys, you are missing important point: the task is not for print to work differently, but to have an extra method that is present only when I can prove that T is iterable..

@sergey-scherbina
Copy link

object TestApp extends App {

  trait Printer[-T] {
    def print(p: T)
  }

  trait EachPrinter[-T] extends Printer[T] {
    def printEach(ps: Iterable[T])
  }

  implicit class PrinterOp[T: Printer](val p: T) {
    def print = implicitly[Printer[T]].print(p)
  }

  implicit class EachPrinterOp[T: EachPrinter](val p: Iterable[T]) {
    def printEach = implicitly[EachPrinter[T]].printEach(p)
  }

  object Printer {

    trait AnyPrinter extends Printer[Any] {
      def print(p: Any) = println(p)
    }

    implicit object AnyPrinter extends AnyPrinter

  }

  object EachPrinter {

    trait AnyEachPrinter extends EachPrinter[Any] with Printer.AnyPrinter {
      def printEach(p: Iterable[Any]) = p foreach print
    }

    implicit object AnyEachPrinter extends AnyEachPrinter

  }

  "hello".print
  //"hello".printEach  // Doesn't compile

  List("hello", "world").print
  List("hello", "world").printEach

}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment