Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a Scala "extractor" use generics on unapply?

Can't I use a generic on the unapply method of an extractor along with an implicit "converter" to support a pattern match specific to the parameterised type?

I'd like to do this (Note the use of [T] on the unapply line),

trait StringDecoder[A] {
  def fromString(string: String): Option[A]
}

object ExampleExtractor {
  def unapply[T](a: String)(implicit evidence: StringDecoder[T]): Option[T] = {
    evidence.fromString(a)
  }
}    

object Example extends App {          

 implicit val stringDecoder = new StringDecoder[String] {
    def fromString(string: String): Option[String] = Some(string)
  }

  implicit val intDecoder = new StringDecoder[Int] {
    def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
  }

  val result = "hello" match {
    case ExampleExtractor[String](x) => x     // <- type hint barfs
  }
  println(result)
}

But I get the following compilation error

Error: (25, 10) not found: type ExampleExtractor case ExampleExtractor[String] (x) => x ^

It works fine if I have only one implicit val in scope and drop the type hint (see below), but that defeats the object.

object Example extends App {

  implicit val intDecoder = new StringDecoder[Int] {
    def fromString(string: String): Option[Int] = Some(string.charAt(0).toInt)
  }

  val result = "hello" match {
    case ExampleExtractor(x) => x
  }
  println(result)
}
like image 414
Toby Avatar asked Jul 31 '15 12:07

Toby


People also ask

What is the use of extractor in Scala?

An extractor in Scala is an object that has a method called unapply as one of its members. The purpose of that unapply method is to match a value and take it apart. Often, the extractor object also defines a dual method apply for building values, but this is not required.

What is the use of apply and Unapply methods in Scala?

An extractor object is an object with an unapply method. Whereas the apply method is like a constructor which takes arguments and creates an object, the unapply takes an object and tries to give back the arguments. This is most often used in pattern matching and partial functions.


1 Answers

A variant of your typed string decoder looks promising:

trait StringDecoder[A] { 
   def fromString(s: String): Option[A] 
}

class ExampleExtractor[T](ev: StringDecoder[T]) {
   def unapply(s: String) = ev.fromString(s)
}
object ExampleExtractor { 
   def apply[A](implicit ev: StringDecoder[A]) = new ExampleExtractor(ev) 
}

then

implicit val intDecoder = new StringDecoder[Int] { 
    def fromString(s: String) = scala.util.Try {
        Integer.parseInt(s)
    }.toOption
}

val asInt = ExampleExtractor[Int]
val asInt(Nb) = "1111"

seems to produce what you're asking for. One problem remains: it seems that trying to

val ExampleExtractor[Int](nB) = "1111"

results in a compiler crash (at least inside my 2.10.3 SBT Scala console).

like image 195
ladrl Avatar answered Oct 23 '22 05:10

ladrl