matanox
matanox

Reputation: 13686

Typed primitives in Scala 2.11

As I see, primitive types like String and Long cannot be extended as they are defined as final. But this is a pity for a type-safe approach. In code that does not revolve around e.g. string manipulation, I prefer to type my data rather than use String, Long, Int, etc: as long as I'm using a type safe language, I'd like my code to really be typed from the ground up.

As per experimentation and as demonstrated on a very old question type aliases do not seem to facilitate this. Currently, I will use things like:

case class DomainType(value: String)

at the cost of having to use .value where the value is needed.

Is there any other language feature been introduced after scala 2.8 or otherwise, that can neatly facilitate type safe sub-typed primitives? are there any object overrides that proxy the underlying value, but still let type matching occur?

Upvotes: 3

Views: 200

Answers (3)

matthias
matthias

Reputation: 356

It seems Tagged types form scalaz would be a good fit.

val a: Int @@ DomainType = Tag(5)

For more information about the usage look at this excellent series of articles: http://eed3si9n.com/learning-scalaz/Tagged+type.html Unfortunately it seems that one has to explicitly call unwrap from scalaz 7.1 onwards but with scalaz7 it was possible to just call something like:

a * 5

But depending on your needs this may still be useful

Upvotes: 0

Gregor Raýman
Gregor Raýman

Reputation: 3081

For the primitive types, you could use the Value Classes. They do not extend the primitive types, but they are internally represented by the JVM primitives and so they avoid in most situations the runtime overhead. You can find more information here.

Upvotes: 2

Lomig Mégard
Lomig Mégard

Reputation: 1828

I don't agree with your way of thinking. Java primitives can't be extended because they are primitives (btw String is not a primitive). They are a direct representation of byte code types. Extending them would make no sense from the compiler perspective.

Implicit value classes

Scala deals with this using the pimp my library pattern, for example with the class RichInt. This permits to add new methods to an existing type (mainly) without object instantiation (through value classes and implicits). Please also have a look to implicit classes.

implicit class DomainType(val o: String) extends AnyVal {
  def myDomainMethod: Int = o.size
}

"hello".myDomainMethod // return 5

Problem, this doesn't allow you to restrict a type as you would like to do with your DomainType. But type classes can help you.

Type classes

Here we want to add a constraint to a type, without inheritance. As said in this link,

As such, type classes allow ad-hoc and retroactive polymorphism. Code that relies on type classes is open to extension without the need to create adapter objects.

The following example shows how the method foo only accepts an argument where an implicit of type DomainType[T] is in scope. It replaces the inheritance you wanted by a retroactive polymorphism. You also keep the benefits from a domain type: The intent is clear, the call type safe and you can add new domain methods.

trait DomainType[T] {
  def myDomainMethod(o: T): Int
}

object DomainType {
  implicit object StringDomainType extends DomainType[String] {
    def myDomainMethod(o: String): Int = o.size
  }
}

def foo[T : DomainType](p: T): Int = {
  implicitly[DomainType[T]].myDomainMethod(p)
}

foo("hello") // return 5
foo(5) // compilation exception

Case classes + implicits

If the only thing that annoys you with your DomainType case class is to call .value, you can always add an implicit that does it for you.

implicit def unwrapDomainType(o: DomainType): String = o.value

Of course this can lower the clarity of the code and I would not recommend it.

Upvotes: 6

Related Questions