La *méthode:
Cela renvoie la projection par défaut - ce que vous décrivez:
  «toutes les colonnes (ou valeurs calculées) qui m'intéressent habituellement ».
Votre table peut avoir plusieurs champs; vous n'avez besoin que d'un sous-ensemble pour votre projection par défaut. La projection par défaut doit correspondre aux paramètres de type de la table.
Prenons-le un à la fois. Sans le <>truc, juste le *:
// First take: Only the Table Defintion, no case class:
object Bars extends Table[(Int, String)]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def * = id ~ name // Note: Just a simple projection, not using .? etc
}
// Note that the case class 'Bar' is not to be found. This is 
// an example without it (with only the table definition)
Juste une définition de table comme celle-ci vous permettra de faire des requêtes comme:
implicit val session: Session = // ... a db session obtained from somewhere
// A simple select-all:
val result = Query(Bars).list   // result is a List[(Int, String)]
la projection par défaut de (Int, String)conduit à un List[(Int, String)] 
pour des requêtes simples comme celles-ci.
// SELECT b.name, 1 FROM bars b WHERE b.id = 42;
val q = 
   for (b <- Bars if b.id === 42) 
     yield (b.name ~ 1)
     // yield (b.name, 1) // this is also allowed: 
                          // tuples are lifted to the equivalent projection.
Quel est le type de q? C'est un Queryà la projection (String, Int). Lorsqu'il est invoqué, il retourne un Listde (String, Int)tuples selon la projection.
 val result: List[(String, Int)] = q.list
Dans ce cas, vous avez défini la projection que vous souhaitez dans la yieldclause de la forcompréhension.
 
Maintenant environ <>et Bar.unapply.
Cela fournit ce que l'on appelle des projections mappées .
Jusqu'à présent, nous avons vu comment slick vous permet d'exprimer des requêtes dans Scala qui renvoient une projection de colonnes (ou de valeurs calculées); Ainsi, lors de l'exécution de ces requêtes, vous devez considérer la ligne de résultat d'une requête comme un tuple Scala . Le type du tuple correspondra à la projection qui est définie (par votre 
 forcompréhension comme dans l'exemple précédent, ou par la *projection par défaut ). C'est pourquoi field1 ~ field2renvoie une projection de Projection2[A, B]où
 Aest le type de field1et Best le type de field2.
q.list.map {
  case (name, n) =>  // do something with name:String and n:Int
}
Queury(Bars).list.map {
  case (id, name) =>  // do something with id:Int and name:String 
}
Nous avons affaire à des tuples, qui peuvent être encombrants si nous avons trop de colonnes. Nous aimerions penser aux résultats non pas comme TupleNmais plutôt comme un objet avec des champs nommés.
(id ~ name)  // A projection
// Assuming you have a Bar case class:
case class Bar(id: Int, name: String) // For now, using a plain Int instead
                                      // of Option[Int] - for simplicity
(id ~ name <> (Bar, Bar.unapply _)) // A MAPPED projection
// Which lets you do:
Query(Bars).list.map ( b.name ) 
// instead of
// Query(Bars).list.map { case (_, name) => name }
// Note that I use list.map instead of mapResult just for explanation's sake.
Comment cela marche-t-il? <>prend une projection Projection2[Int, String]et renvoie une projection mappée sur le type Bar. Les deux arguments Bar, Bar.unapply _
indiquent clairement comment cette (Int, String)projection doit être mappée à une classe de cas.
Il s'agit d'une cartographie bidirectionnelle; Barest le constructeur de la classe case, c'est donc les informations nécessaires pour passer de (id: Int, name: String)à a Bar. Et unapply
si vous l'avez deviné, c'est l'inverse.
D'où unapplyvient-il? Il s'agit d'une méthode Scala standard disponible pour toute classe de cas ordinaire - il suffit de définir Barun Bar.unapplyqui est un extracteur qui peut être utilisé pour récupérer le idet avec namelequel le a
 Barété construit:
val bar1 = Bar(1, "one")
// later
val Bar(id, name) = bar1  // id will be an Int bound to 1,
                          // name a String bound to "one"
// Or in pattern matching
val bars: List[Bar] = // gotten from somewhere
val barNames = bars.map {
  case Bar(_, name) => name
}
val x = Bar.unapply(bar1)  // x is an Option[(String, Int)]
Ainsi, votre projection par défaut peut être mappée à la classe de cas que vous vous attendez le plus à utiliser:
object Bars extends Table[Bar]("bar") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def name = column[String]("name")
  def * = id ~ name <>(Bar, Bar.unapply _)
}
Ou vous pouvez même l'avoir par requête:
case class Baz(name: String, num: Int)
// SELECT b.name, 1 FROM bars b WHERE b.id = 42;
val q1 = 
   for (b <- Bars if b.id === 42) 
     yield (b.name ~ 1 <> (Baz, Baz.unapply _))
Ici, le type de q1  est un Queryavec une projection mappée vers Baz. Lorsqu'elle est appelée, elle renvoie un Listdes Bazobjets:
 val result: List[Baz] = q1.list
 
Enfin, en passant , les .?offres Option Lifting - la manière Scala de traiter des valeurs qui peuvent ne pas l'être.
 (id ~ name)   // Projection2[Int, String] // this is just for illustration
 (id.? ~ name) // Projection2[Option[Int], String]
Ce qui, en conclusion, fonctionnera bien avec votre définition originale de Bar:
case class Bar(id: Option[Int] = None, name: String)
// SELECT b.id, b.name FROM bars b WHERE b.id = 42;
val q0 = 
   for (b <- Bars if b.id === 42) 
     yield (b.id.? ~ b.name <> (Bar, Bar.unapply _))
q0.list // returns a List[Bar]
 
En réponse au commentaire sur la façon dont Slick utilise les forcompréhensions:
D'une manière ou d'une autre, les monades parviennent toujours à apparaître et demandent à faire partie de l'explication ...
Car les compréhensions ne sont pas spécifiques aux collections uniquement. Ils peuvent être utilisés sur n'importe quel type de Monad , et les collections ne sont que l'un des nombreux types de types de monades disponibles dans Scala.
Mais comme les collections sont familières, elles constituent un bon point de départ pour une explication:
val ns = 1 to 100 toList; // Lists for familiarity
val result = 
  for { i <- ns if i*i % 2 == 0 } 
    yield (i*i)
// result is a List[Int], List(4, 16, 36, ...)
Dans Scala, un pour la compréhension est un sucre syntaxique pour les appels de méthode (éventuellement imbriqués): Le code ci-dessus est (plus ou moins) équivalent à:
ns.filter(i => i*i % 2 == 0).map(i => i*i)
En gros, quoi que ce soit avec filter, map, des flatMap
méthodes (autrement dit, un Monad ) peut être utilisé dans une 
 forcompréhension à la place de ns. Un bon exemple est la monade Option . Voici l'exemple précédent où la même forinstruction fonctionne à la fois sur les 
 monades Listet sur les Optionmonades:
// (1)
val result = 
  for { 
    i <- ns          // ns is a List monad
    i2 <- Some(i*i)  // Some(i*i) is Option
      if i2 % 2 == 0 // filter
  } yield i2
// Slightly more contrived example:
def evenSqr(n: Int) = { // return the square of a number 
  val sqr = n*n         // only when the square is even
  if (sqr % 2 == 0) Some (sqr)
  else None
}
// (2)
result = 
  for { 
    i <- ns  
    i2 <- evenSqr(i) // i2 may/maynot be defined for i!
  } yield i2
Dans le dernier exemple, la transformation ressemblerait peut-être à ceci:
// 1st example
val result = 
  ns.flatMap(i => Some(i*i)).filter(i2 => i2 %2 ==0)
// Or for the 2nd example
result = 
  ns.flatMap(i => evenSqr(i)) 
Dans Slick, les requêtes sont monadiques - ce ne sont que des objets avec les méthodes map, flatMapet filter. Donc, la forcompréhension (montrée dans l'explication de la *méthode) se traduit simplement par:
val q = 
  Query(Bars).filter(b => b.id === 42).map(b => b.name ~ 1)
// Type of q is Query[(String, Int)]
val r: List[(String, Int)] = q.list // Actually run the query
Comme vous pouvez le voir, flatMap, mapet filtersont utilisés pour générer une Querypar la transformation répétée Query(Bars)
avec chaque appel filteret map. Dans le cas des collections, ces méthodes itèrent et filtrent la collection, mais dans Slick, elles sont utilisées pour générer du SQL. Plus de détails ici:
 Comment Scala Slick traduit-il le code Scala en JDBC?