19

http://woss.name/2012/04/02/retrieving-bigdecimals-from-a-database-with-anorm-scala/

object Site {
  val allFieldsParser = {
    get[Pk[Long]]("sites.id") ~     // Help me parse this syntax
    get[String]("sites.name") ~
    get[BigDecimal]("sites.latitude") ~
    get[BigDecimal]("sites.longitude") map {
      case id ~ name ~ latitude ~ longitude =>
        Site(id, name, latitude, longitude)
    }
  }

  def findAll(): Seq[Site] = {
    DB.withConnection { implicit connection =>
      SQL("SELECT * FROM sites").as(Site.allFieldsParser *)
    }
  }
}
Ben James
  • 121,135
  • 26
  • 193
  • 155
ripper234
  • 222,824
  • 274
  • 634
  • 905
  • 2
    This notation was derived from parser combinators. Please, see http://stackoverflow.com/questions/6818390/understanding-the-tilde-in-scalas-parser-combinators In short, you may mentally replace `~` with `&`, so such structure will look like a pattern (you know, like a pattern in regex). You're defining high level representation using particular parts (combined into solid structure with ~'s) and then parser either succedes, and you got structure according your scheme, or fails. For example, to match algebraic expression one could write something like `Number ~ Operation ~ Number ...` – om-nom-nom Jun 13 '13 at 23:25
  • @om-nom-nom So, why did you answer in the comment? – Daniel C. Sobral Jun 13 '13 at 23:29
  • 1
    @DanielC.Sobral because I'm unsure whether the question is about technical implementation (which was already explained by gzm0) or about semantical stuff. – om-nom-nom Jun 13 '13 at 23:33
  • 3
    Actually it's the operator symbol for tilda swinton. – som-snytt Jun 13 '13 at 23:46

2 Answers2

29

In your example, ~ is being used in two different ways to mean two different things. In the first part you have

get[Pk[Long]]("sites.id") ~     // Help me parse this syntax
get[String]("sites.name") ~
get[BigDecimal]("sites.latitude") ~

etc. As it has already been pointed out, this is just method invocation, it is the same as

get[Pk[Long]]("sites.id").~(get[String]("sites.name").~(...

You can look at the definition of this method in the anorm source. It is a method on a RowParser[A] (a parser that parses an A, which takes a RowParser[B] (a parser that parses a B) and returns a parser that parses a A ~ B. This A ~ B is a second meaning for ~. This is now referring not to a method, but to a case class defined in the same file here.

case class ~[+A, +B](_1: A, _2: B)

This is just a idiosyncratic way of referring to a class ~[A,B]. At the type level, when you have a two argument type constructor, you can use the name of the class in infix notation. This isn't anything special about ~, it would work with any two argument type constructor. If you had trait Foo[A,B] you could refer to that as A Foo B. Analogously, in pattern matching, variables a and b can be bound using the syntax a Foo b, which is referred to as an Infix Operation Pattern in section 8.1.10 of the language specification.

In the second part of your example you have:

case id ~ name ~ latitude ~ longitude =>

This is pattern matching on these ~ case clases which are the result of running the parse you constructed above. So this is really just a nicer way of writing:

case ~(~(~(id, name), latitude), longitude) =>
jaltekruse
  • 313
  • 2
  • 5
stew
  • 11,276
  • 36
  • 49
6

In Scala

a ~ b

means

a.~(b)

So it calls the method ~ on a and gives b as an argument. Also note that any operator not ending with : is left-associative.

Your example revisited would be:

get[Pk[Long]]("sites.id").~(     // Help me parse this syntax
get[String]("sites.name").~(
get[BigDecimal]("sites.latitude").~(
get[BigDecimal]("sites.longitude")))) map {
  case id ~ name ~ latitude ~ longitude =>
    Site(id, name, latitude, longitude)
}
gzm0
  • 14,752
  • 1
  • 36
  • 64
  • 1
    More generally, `a whatever b` means `a.whatever(b)` (with the exception of some keywords, primitive operators, and the left-associative indicator you already mentioned). – Kristian Domagala Jun 13 '13 at 23:20