scalascalaz

What is this @@ operator defined in scalaz?


When browsing a piece of Scala code at aws-scala by Atlassian you can find the following line:

type QueueURL = String @@ QueueURL.Marker

I am new to Scala, so I might be wrong, but the @@ (double at-sign) doesn't seem like a standard built-in Scala operator. Moreover, an observant reader will find it imported from 'scalaz' library:

import scalaz.{ Tag, @@ }

What does the @@ do? And why is it used?

As mentioned in the comments, the actual definition is:

 type @@[A, T] = A

which might be some hint.


Solution

  • The idea of tag is that quite often you don't want to use raw Longs, Ints and so on everywhere - well, you want to use them in your code, but you don't want to pass them on interface level:

    def fetchUsers(numberOfUsers: Int, offset: Int): Seq[User]
    

    here you have to used named parameters in order to make sure that you didn't swap the order of arguments. Additionally someone might make the mistake of overriding it with wrong order:

    override def fetchUsers(offset: Int, numberOfUsers: Int): Seq[User]
    

    To avoid that you can use different types for both arguments. One way yo do it is to use case class with AnyVal - if you follow few rules it is optimized by compiler to primitives. Tags are alternative method to introduce new types for some common types (that might not necessarily be primitives). @@ is defined as

    type @@[A, T] = A
    

    as you noticed. So you could define new type as:

    @@[String, QueueURL.Marker]
    

    but because Scala allow us to use infix syntax on types, we can write it also as:

    String @@ QueueURL.Marker
    

    It is especially useful if you make dependency injection with e.g. implicits or Macwire - there arguments are fetch based only on types, and so having distinguished type for each injectable value is a must (on a side-note Macwire implements it's own version of @@ - they did it slightly different way, but it serves the same purpose).

    Then you can end up with a code like this:

    def fetchUsers(numberOfUsers: Int @@ UsersNumber, offset: Int @@ Offset): Seq[User]
    

    or:

    type UsersNumber = Int @@ UsersNumberTag
    type UsersOffset = Int @@ UsersOffsetTag
    
    def fetchUsers(numberOfUsers: UsersNumber, offset: UsersOffset): Seq[User]
    

    I also saw this variant:

    type UsersNumber[T] = T @@ UsersNumberTag
    type UsersOffset[T] = T @@ UsersOffsetTag
    
    def fetchUsers(numberOfUsers: UsersNumber[Int], offset: UsersOffset[Int]): Seq[User]
    

    Looking at the aws-scala code I assume they wanted to achieve the first property - being able to distinguish different usages of some common types (like String) and use compiler to check if they didn't make a mistake.

    Example in ammonite:

    @ type @@[A, T] = A with T
    defined type @@
    
    @ trait Username
    defined trait Username
    
    @ def passUsername(username: String @@ Username): Unit = ()
    defined function passUsername
    
    @ passUsername("test")
    cmd3.sc:1: type mismatch;
      String|String with Username
    val res3 = passUsername("test")
                            ^
    Compilation Failed