What does the tilde (~) mean in this Scala example?
Asked Answered
D

2

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 *)
    }
  }
}
Desta answered 13/6, 2013 at 22:58 Comment(4)
This notation was derived from parser combinators. Please, see #6818890 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 ...Semiyearly
@Semiyearly So, why did you answer in the comment?Jacaranda
@DanielC.Sobral because I'm unsure whether the question is about technical implementation (which was already explained by gzm0) or about semantical stuff.Semiyearly
Actually it's the operator symbol for tilda swinton.Octroi
M
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) =>
Mersey answered 13/6, 2013 at 23:52 Comment(0)
E
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)
}
Embolic answered 13/6, 2013 at 23:4 Comment(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).Equuleus

© 2022 - 2024 — McMap. All rights reserved.