You should never extend AnyVal, unless you're using that for defining extension methods. The feature is a hack, because it has to workaround the JVM's lack of value classes, hence it has non-intuitive behavior and it probably doesn't work as you think it does.
In particular if you ever have to declare the type anywhere (or in other words, whenever you treat it as a type), like in:
case class Person(id: PersonId)
That's going to be an allocated PersonId instance. And the limitations don't stop there. You'll also get instances allocated when doing pattern matching, or when working with generic functions. Heck, if you'll actually measure the performance, you'll soon discover that the JVM will allocate more on the heap and not less. And there goes the primary use-cases out the window. So you might as well have it as a normal class and avoid the gotchas and your colleagues cursing ;-)
I admit that I am not an expert on Scala, but I am confused about your "Person" example. I thought that this was exactly the common use case that wouldn't cost you an allocation.
According to http://docs.scala-lang.org/overviews/core/value-classes.html:
A value class is actually instantiated when:
a value class is treated as another type.
a value class is assigned to an array.
doing runtime type tests, such as pattern matching.
In particular if you ever have to declare the type anywhere (or in other words, whenever you treat it as a type), like in:
That's going to be an allocated PersonId instance. And the limitations don't stop there. You'll also get instances allocated when doing pattern matching, or when working with generic functions. Heck, if you'll actually measure the performance, you'll soon discover that the JVM will allocate more on the heap and not less. And there goes the primary use-cases out the window. So you might as well have it as a normal class and avoid the gotchas and your colleagues cursing ;-)