Dan Shryock
Dan Shryock

Reputation: 355

Can extractors be customized with parameters in the body of a case statement (or anywhere else that an extractor would be used)?

Basically, I would like to be able to build a custom extractor without having to store it in a variable prior to using it.

This isn't a real example of how I would use it, it would more likely be used in the case of a regular expression or some other string pattern like construct, but hopefully it explains what I'm looking for:

def someExtractorBuilder(arg:Boolean) = new {
  def unapply(s:String):Option[String] = if(arg) Some(s) else None
}

//I would like to be able to use something like this 
val {someExtractorBuilder(true)}(result) = "test"
"test" match {case {someExtractorBuilder(true)}(result) => result }

//instead I would have to do this:
val customExtractor = someExtractorBuilder(true)
val customExtractor(result) = "test"
"test" match {case customExtractor(result) => result}

When just doing a single custom extractor it doesn't make much difference, but if you were building a large list of extractors for a case statement, it could make things more difficult to read by separating all of the extractors from their usage.

I expect that the answer is no you can't do this, but I thought I'd ask around first :D

Upvotes: 18

Views: 1457

Answers (5)

Espen Brekke
Espen Brekke

Reputation: 404

Though what you are asking isn't directly possible,
it is possible to create an extractor returning a contaner
that gets evaluated value in the if-part of the case evaluation. In the if part it
is possible to provide parameters.

object DateExtractor {
  def unapply(in: String): Option[DateExtractor] = Some(new DateExtractor(in));
}

class DateExtractor(input:String){
  var value:LocalDate=null;
  def apply():LocalDate = value;
  def apply(format: String):Boolean={
    val formater=DateTimeFormatter.ofPattern(format);
    try{
      val parsed=formater.parse(input, TemporalQueries.localDate());
      value=parsed
      true;
    } catch {
      case e:Throwable=>{
        false
      }
    }
  }
}

Usage:

object DateExtractorUsage{
  def main(args: Array[String]): Unit = {
    "2009-12-31" match {
      case DateExtractor(ext) if(ext("dd-MM-yyyy"))=>{
        println("Found dd-MM-yyyy date:"+ext())
      }
      case DateExtractor(ext) if(ext("yyyy-MM-dd"))=>{
        println("Found yyyy-MM-dd date:"+ext())
      }
      case _=>{
        println("Unable to parse date")
      }
    }
  }
}

This pattern preserves the PartialFunction nature of the piece of code.
I find this useful since I am quite a fan of the collect/collectFirst methods, which take a partial function as a parameter and typically does not leave room for precreating a set of extractors.

Upvotes: 0

cchantep
cchantep

Reputation: 9168

Late but there is a scalac plugin in one of my lib providing syntax ~(extractorWith(param), bindings):

x match {
  case ~(parametrizedExtractor(param)) => 
    "no binding"
  case ~(parametrizedExtractor(param), (a, b)) =>
    s"extracted bindings: $a, $b"
}

https://github.com/cchantep/acolyte/blob/master/scalac-plugin/readme.md

Upvotes: 2

Suma
Suma

Reputation: 34423

One can customize extractors to certain extent using implicit parameters, like this:

object SomeExtractorBuilder {
  def unapply(s: String)(implicit arg: Boolean): Option[String] = if (arg) Some(s) else None
}

implicit val arg: Boolean = true
"x" match {
  case SomeExtractorBuilder(result) =>
    result
}

Unfortunately this cannot be used when you want to use different variants in one match, as all case statements are in the same scope. Still, it can be useful sometimes.

Upvotes: 2

Adriaan Moors
Adriaan Moors

Reputation: 4316

Parameterising extractors would be cool, but we don't have the resources to implement them right now.

Upvotes: 8

retronym
retronym

Reputation: 55028

Nope.

8.1.7 Extractor Patterns

An extractor pattern x (p 1 , . . . , p n ) where n ≥ 0 is of the same syntactic form as a constructor pattern. However, instead of a case class, the stable identifier x denotes an object which has a member method named unapply or unapplySeq that matches the pattern.

Upvotes: 4

Related Questions