rmin
rmin

Reputation: 1128

Scala - how to create a single implicit that can be used for a type constructor

I'm trying to write a method which uses the isEmpty method on types String, Option and List. These classes don't share a common base trait with that method, so I've tried to pass an implicit EmptyChecker in with them:

  trait EmptyChecker[Field] {
    def isEmpty(data: Field): Boolean
  }

  implicit val StringEmptyChecker: EmptyChecker[String] = new EmptyChecker[String] {
    def isEmpty(string: String): Boolean = string.isEmpty
  }

  def printEmptiness[Field](field: Field)(implicit emptyChecker: EmptyChecker[Field]): Unit = {
    if (emptyChecker.isEmpty(field))
      println("Empty")
    else
      println("Not empty")
  }

  printEmptiness("abc") // Works fine

The String empty checker works fine, but I've hit problems with making empty checkers for type constructors like Option and List.

For example, Option doesn't work:

  implicit val OptionChecker: EmptyChecker[Option[_]] = new EmptyChecker[Option[_]] {
    def isEmpty(option: Option[_]): Boolean = option.isEmpty
  }

  // Both fail compilation: "could not find implicit value for parameter emptyChecker: EmptyChecker[Some[Int]]
  printEmptiness(Some(3))
  printEmptiness[Option[Int]](Some(3))      

If I use a specific Option[Int] checker, it works a little better, but is a bit ugly:

  implicit val OptionIntChecker: EmptyChecker[Option[Int]] = new EmptyChecker[Option[Int]] {
    def isEmpty(optionInt: Option[Int]): Boolean = optionInt.isEmpty
  }

  // Fails like above:
  printEmptiness(Some(3))

  // Passes compilation:
  printEmptiness[Option[Int]](Some(3))

So my question is: is it possible to make a single EmptyChecker for each Option and List type and have them work with my method without needing to explicitly declare the type whenever I call it? I'm trying to get a type safe duck typing effect.

I'm using scala 2.11.6.

Thanks in advance!

Upvotes: 2

Views: 49

Answers (1)

Hugh
Hugh

Reputation: 8932

The source of your problem is that the type of Some(1) is Some[Int], not Option[Int]. There are a couple of ways around this; you can explicitly upcast the expression with a type ascription: printEmptiness(Some(3): Option[Int]). Alternatively, you can define a helper method to do this for you automatically, and if you're using Scalaz, there's one of these provided:

import scalaz.syntax.std.option._
printEmptiness(3.some)

Furthermore if you do use Scalaz, you may find looking at the PlusEmpty/ApplicativePlus/MonadPlus type classes useful.

Upvotes: 3

Related Questions