Bharadwaj
Bharadwaj

Reputation: 1351

Scala types and compilation

I was using shapeless tags and wrote code similar to this -

import shapeless.tag
import shapeless.tag.@@

object Typeplay {
  trait StringTrait
  type MyString = String @@ StringTrait

  case class StringClass(mps: MyString)
  val stringClass = StringClass(tag[StringTrait]("test"))
}

And this code would not compile. Scala compiler complains about the last line of code saying -

[error]  found   : String("test")
[error]  required: shapeless.tag.Tagged[in.bharathwrites.Typeplay.StringTrait] with String
[error]   val stringClass = StringClass(tag[StringTrait]("test"))

I could not understand what was I doing wrong. So I made a small change to my code -

import shapeless.tag
import shapeless.tag.@@

object Typeplay {
  trait StringTrait
  type MyString = String @@ StringTrait

  case class StringClass(mps: MyString)

  val stringTag = tag[StringTrait]("test")
  val stringClass = StringClass(stringTag)
}

Which is basically just using an explicit variable for the tagging. And this code compiles!!

How can this be? Why does the first program not compile and the second one does?

Upvotes: 5

Views: 153

Answers (1)

Aivean
Aivean

Reputation: 10882

It seems that type alias confuses type inference in this case. If you provide explicit types to the tag method, everything works fine:

StringClass(tag[StringTrait][String]("test"))

or

StringClass(tag[StringTrait]("test"):String @@ StringTrait)

or if you declare param StringClass directly:

case class StringClass(mps: String @@ StringTrait)

StringClass(tag[StringTrait]("test"))

Apparently the limitation of scala compiler.


UPD. Frankly, I can't tell what exact limitation of type inference you hit here. My search for relevant open bugs was fruitless.

Regarding the first example. Everything becomes clear if you look at the implementation of the tag:

object tag {
  def apply[U] = new Tagger[U]

  trait Tagged[U]
  type @@[+T, U] = T with Tagged[U]

  class Tagger[U] {
    def apply[T](t : T) : T @@ U = t.asInstanceOf[T @@ U]
  }
}

So, when you do this:

tag[StringTrait][String]("test")

You basically do this:

tag.apply[StringTrait].apply[String]("test")

Upvotes: 1

Related Questions