Marker trait used to mark a type alias as being a distinct type alias (DTA).
Marker trait used to mark a type alias as being a distinct type alias (DTA). A DTA is an alternative to the Scala value-class (http://docs.scala-lang.org/overviews/core/value-classes.html) that never needs to box (or unbox) since type aliases are eliminated in byte code.
A DTA is declared with a unique empty trait "tag" to make the type alias distinct in the type system. Since the DTA is a distinct type from its alias, implicit generics (e.g. type-classes) of the DTA can be resolved separately from the aliased type. Implicits resolving a normal type-alias will always resolve to an instance for the aliased type.
Example:
type Age1 = Int implicitly[Format[Age1] ] // resolves to Format[Int] b/c Age1 isn't distinct trait AgeTag // empty "tag" trait type Age = Int with AgeTag implicitly[Format[Age] ] // resolves to Format[Age] b/c Age is distinct
Unlike value-classes, which are elided by the compiler under most circumstances, DTAs are always elided at runtime. Value-classes used in any kind of generic, such as List or Option, cannot be elided by the compiler and boxing/unboxing penalties are incurred.
This means if a conversion from a generic of the value-class type to the underlying type is required (or vice-versa), a map operation must be used to box or unbox the value-class. This incurs a O(n) penalty simply to cast between the value-class and the underlying type. Because DTAs are type aliases for their underlying type the compiler treats a generic of a DTA as a generic of the underlying type without any impact on runtime.
While covariant generics will automatically cast from a generic of the DTA to its underlying type, casting from the underlying type to the DTA is not supported directly. Instead an implicit generic converter must be imported:
Example generic converter:
implicit def ma_to_mv[M[_],V < : IsDistinctTypeAlias[A],A]( ma: M[A] ) : M[V] = ma.asInstanceOf[M[V] ]
Note: this converter is declared in codetools package.scala
To declare a DTA, first create an empty trait tag. Next, declare a type alias for the aliased type but that also extends the tag and the marker trait IsDistinctTypeAlias. Finally, declare an implicit def constructor method that mimics the apply method of a companion object.
Example:
trait AgeTag type Age = Int with AgeTag with IsDistinctTypeAlias[Int] implicit def Age(i: Int) = i.asInstanceOf[Age]
Note1: The IsDistinctTypeAlias marker trait self-documents the DTA and is used by the implicit def converters to prevent ambiguous implicit resolution errors. Note2: While it is possible to declare an object for the DTA, it will not be treated as a companion object for the DTA by the compiler (implicit generics in the object will not resolve implicitly like normal companion objects)
type aliased by the DTA
A base trait for a user-defined value-class that standardizes the name of the value-class val to "underlying" and the toString implementation to underlying.toString.
A base trait for a user-defined value-class that standardizes the name of the value-class val to "underlying" and the toString implementation to underlying.toString. This allows creating an implicit conversion from an instance of any value-class to its underlying representation.
For details on value-class see: http://docs.scala-lang.org/overviews/core/value-classes.html
Example:
implicit class Name(underlying: String) extends AnyVal with IsValueType[String]
Note1: When creating a value-class for String, it is necessary to create an implicit view to StringOps to use the Scala extended String methods on instances of the value-class. Example:
object Name { import scala.collection.immutable.StringOps implicit def stringOps_Name(n: Name) = new StringOps(n.underlying) }
Note2: while almost every method on underlying can be used on the value-class without special code, the equals method is a special case that still requires wrapping the right-hand type in the value-class to match the other side. Ex:
Name("Hal") == "Hal" // always returns false + compiler warning Name("Hal") == Name("Hal") // works correctly
type of underlying value class (Note: this parameter does not require inheritance from AnyVal since this would prevent using the trait with java.lang.String which does not inherit AnyVal)
Implicitly convert any generic M[V] to M[A] where V is a distinct type alias of A
Implicitly convert any generic M[V] to M[A] where V is a distinct type alias of A
generic type
DTA type
underlying type of DTA
generic instance
instance cast to M[V]
Implicitly convert any generic M[A] to M[V] where V is a distinct type alias of A
Implicitly convert any generic M[A] to M[V] where V is a distinct type alias of A
generic type
DTA type
underlying type of DTA
generic instance
instance cast to M[V]
Implicitly convert any generic M[A,B] to a M[V,B] where V is a distinct type alias of A
Implicitly convert any generic M[A,B] to a M[V,B] where V is a distinct type alias of A
type of generic instance
underlying type of DTA
DTA type
some type
generic instance
instance cast to M[V,B]
Implicitly convert any generic M[A,B] to a M[A,V] where V is a distinct type alias of B
Implicitly convert any generic M[A,B] to a M[A,V] where V is a distinct type alias of B
type of generic instance
some type
DTA type
underlying type of DTA
generic instance
instance cast to M[A,V]
Implicitly convert a value class to its underlying type
Implicitly convert a value class to its underlying type
the underlying type
a value-class derived from IsValueClass
the wrapped value