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) =>
~
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 likeNumber ~ Operation ~ Number ...
– Semiyearly