2016-01-10 2 views
0

je l'ai appris Extracteurs du livre d'escalier:Comment l'extracteur fonctionne-t-il lorsque la fonction `unapply` renvoie une valeur booléenne au lieu de l'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.") 
    } 

C'est assez simple. Mais aujourd'hui, j'ai lu un livre sur le cadre de lecture de ce code:

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) 
     } 
    } 

Comment fonctionne le extracteur lorsque la fonction retourne unapply Boolean au lieu de l'option? Comment fonctionne &, Accepts.Xml ici?

Répondre

0

Ok, j'ai trouvé un moyen de comprendre cela en faisant un exemple minimal:

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 
    } 
} 

La chose étrange est que lorsque vous utilisez DividedBy(15)() (commentée ci-dessus), le code ne compilera pas.


Mise à jour:

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" 
    } 
} 

Après quelques lectures de vieilles notes sur le livre d'escalier maintenant j'ai une compréhension plus claire de cela. La classe de cas est une usine d'extracteur.

+0

Les extracteurs doivent être définis dans un objet compagnon, pas la classe elle-même. – PhilBa

+0

Pour en revenir à votre exemple Play, il semble être utilisé pour spécifier les MIME que vous voulez accepter, et comment vous voulez les gérer. – PhilBa

+0

J'ai réécrit un peu votre exemple et l'ai posté sur github: https://gist.github.com/Phil-Ba/7725cba259973834ae70#file-extractor-scala – PhilBa

1

Je peux vraiment vous parler de la structure de jeu, mais si elle est utilisée dans un modèle correspondant à un extracteur renvoyant un booléen, cela signifie que le modèle correspond. Ainsi, si un extracteur renvoie true, cela signifie que le motif correspond à la valeur. Ceci est un bon lien sur les extracteurs et couvre également ce cas: http://danielwestheide.com/blog/2012/11/21/the-neophytes-guide-to-scala-part-1-extractors.html

En général on utilise des extracteurs pour deux cas d'utilisation:

1) destructing un objet, ce qui signifie retourner une ou plusieurs valeurs qui représentent l'état de objet donné

2) Vous pouvez également utiliser des extracteurs pour transformer un objet en objet d'un autre type lors de l'appariement de formes. J'ai fait un petit exemple pour ce cas:

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() 
}