Как работает экстрактор, когда функция `unapply` возвращает Boolean вместо Option?

Я узнал об экстракторах из книги на лестнице:

    object Twice {
      def apply(x: Int) = x * 2
      def unapply(x: Int) = if(x % 2 == 0) Some(x / 2) else None
    }
    // outside pattern mathcing, Twice.apply(21) is called
    val x = Twice(21)
    x match {
        // inside pattern matching, Twice.unapply(x) is called,
        // the result Some(21) is matched against y,
        // y gets the value 21
      case Twice(y) => println(x + " is twice " + y)
      case _ => println(x + " is odd.")
    }

Это довольно просто. Но сегодня я прочитал из какой-то книги по Play Framework этот код:

trait RequestExtractors extends AcceptExtractors {

  //Convenient extractor allowing to apply two extractors. 
  object & { 
    def unapply(request: RequestHeader): Option[(RequestHeader, RequestHeader)] = Some((request, request)) 
  } 

}

//Define a set of extractors allowing to pattern match on the Accept HTTP header of a request 
trait AcceptExtractors {

  //Common extractors to check if a request accepts JSON, Html, etc. 
  object Accepts { 
    import play.api.http.MimeTypes 
    val Json = Accepting(MimeTypes.JSON) 
    val Html = Accepting(MimeTypes.HTML) 
    val Xml = Accepting(MimeTypes.XML) 
    val JavaScript = Accepting(MimeTypes.JAVASCRIPT) 
  } 

}

//Convenient class to generate extractors checking if a given mime type matches the Accept header of a request. 
case class Accepting(val mimeType: String) {
  def unapply(request: RequestHeader): Boolean = request.accepts(mimeType) 
  def unapply(mediaRange: play.api.http.MediaRange): Boolean = mediaRange.accepts(mimeType) 
}


def fooBar = Action { 
    implicit request => 
      val xmlResponse: Node = <metadata> 
        <company>TinySensors</company> 
        <batch>md2907</batch> 
      </metadata> 

      val jsonResponse = Json.obj("metadata" -> Json.arr( 
        Json.obj("company" -> "TinySensors"), 
        Json.obj("batch" -> "md2907")) 
      ) 

      render { 
        case Accepts.Xml() => Ok(xmlResponse) 
        case Accepts.Json() & Accepts.JavaScript() => Ok(jsonResponse) 
      }
  }

Как работает экстрактор, когда unapply функция возвращает логическое значение вместо Option? Как &, Accepts.Xml работать здесь?

2 ответа

Хорошо, я нашел способ понять это, сделав минимальный пример:

object Unapply {

  case class DividedBy(val number: Int) {
    def unapply(divider: Int): Boolean = number % divider == 0
    def unapply(divider: Double): Boolean = number % divider.toInt == 0
  }

  val x = DividedBy(15)
  // y should be true
  val y = 5 match {
  //    case DividedBy(15)() => true
    case x() => true
    case _ => false
  }
}

Странная вещь в том, что когда вы используете DividedBy(15)() (закомментировано выше), код не будет компилироваться.


Обновить:

object Unapply {
  case class Division(val number: Int) {
//    def unapply(divider: Int): Boolean = number % divider == 0
    def unapply(divider: Int): Option[(Int, Int)] = if (number % divider == 0) Some(number/divider, 0) else None
    def unapply(divider: Double): Boolean = number % divider.toInt == 0
  }

  object Division {
    def apply(number: Int) = new Division(number)
  }

  val divisionOf15 = Division(15)
  // y should be true
  val y = 5 match {
  //    case DividedBy(15)() => true
    case divisionOf15(z, w) => s"$z, $w"
    case _ => s"Not divisible"
  }

  val z = 5.0 match {
    case divisionOf15() => "Divisible"
    case _ => "Not divisible"
  }
}

Прочитав несколько старых заметок на лестничной книжке, теперь я стал лучше понимать это. Класс Case - это экстракторная фабрика.

Я действительно могу рассказать вам об игровой платформе, но при использовании в сопоставлении с шаблоном экстрактор, возвращающий логическое значение, означает, что шаблон соответствует. Таким образом, если экстрактор возвращает true, это означает, что шаблон соответствует значению. Это хорошая ссылка на экстракторы, которая также охватывает этот случай: http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html

Обычно вы используете экстракторы для двух вариантов использования:

1) Уничтожение объекта, что означает возврат одного или нескольких значений, которые представляют состояние данного объекта

2) Вы также можете использовать экстракторы, чтобы превратить объект в объект другого вида во время сопоставления с образцом. Я сделал небольшой пример для этого случая:

class Division(val number: Int) {
}

object Division {
    def unapply(divider: Division): Boolean = divider.number != 0

    def unapply(divider: Int): Option[Division] = if (divider != 0) Some(new Division(divider)) else None
}

val divident = 15
val divider = 5
val y = divider match {
    case Division(notZero) => divident / notZero.number //notZero is of type Division
    case _ => throw new IllegalArgumentException()
}
Другие вопросы по тегам