21

In Haskell, I believe that it is possible to alias a type in such a way that the compiler does not allow references between the aliased type and the unaliased type. According to this stack overflow question, one can use Haskell's newtype like so:

newtype Feet = Feet Double
newtype Cm   = Cm   Double

where Feet and Cm will behave like Double values, but attempting to multiply a Feet value and a Cm value will result in a compiler error.

EDIT: Ben pointed out in the comments that this above definition in Haskell is insufficient. Feet and Cm will be new types, on which there will be no functions defined. Doing a bit more research, I found that the following will work:

newtype Feet = Feet Double deriving (Num)
newtype Cm   = Cm   Double deriving (Num)

This creates a new type that derives from the existing Num type (requires using switch: -XGeneralizedNewtypeDeriving). Of course, these new types will be even more valuable deriving from other types such as Show, Eq, etc. but this is the minimum required to correctly evaluate Cm 7 * Cm 9.

Both Haskell and Scala have type, which simply aliases an existing type and allows nonsensical code such as this example in Scala:

type Feet = Double
type Cm = Double

val widthInFeet: Feet = 1.0
val widthInCm: Cm = 30.48

val nonsense = widthInFeet * widthInCm

def getWidthInFeet: Feet = widthInCm

Does Scala have a newtype equivalent, assuming that this does what I think it does?

Community
  • 1
  • 1
rybosome
  • 5,046
  • 7
  • 44
  • 64
  • That's not actually how `newtype` works in Haskell. It creates a **new** type, which by default has no functions defined on it. So `Feet` and `Cm` values in your examples will not be able to be multiplied at all until you implement multiplication for them. Types declared with `newtype` will be *represented* identically to the wrapped type, which means that there is zero runtime cost of implementing operations on the `newtype` by simply unwrapping and passing through to the operation on the wrapped type. But that's really an optimization, irrelevant to what `newtype` actually means. – Ben Nov 15 '12 at 01:41
  • Thanks for the information, I updated the question to reflect that. Still a Haskell noobie, though very interested in getting better. =) – rybosome Nov 15 '12 at 01:53
  • 8
    You should note that, while adding Cm to Cm to get Cm makes sense, multiplying Cm by Cm would get you Cm², which measures area rather than length. Using a Haskell-style type system to keep track of units can be pretty tricky to do properly. – shachaf Nov 15 '12 at 04:53
  • 4
    In addition to what @shachaf said, you might want to look at the [dimensional package](http://hackage.haskell.org/package/dimensional-0.10.2). That way you can multiply 1 foot and 30.48 centimeter and get 929.0304 square centimeters. – yatima2975 Nov 15 '12 at 06:54

4 Answers4

14

Another option would be to use value classes. These create a wrapper around an underlying type which is converted into direct access to the raw type at compile time, with methods on the class being converted into static calls on an associated companion object. For example:

class CM(val quant : Double) extends AnyVal {
  def +(b : CM) = new CM(quant + b.quant)
  def *(b : Int) = new CM(quant * b)
}
Submonoid
  • 2,809
  • 2
  • 20
  • 25
9

Yeah you using something known as Unboxed Tagged Types in scala.

This is how Tagged is defined:

type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]

This allows you to do something like this

sealed trait Feet

def Feet[A](a: A): A @@ Feet = Tag[A, Feet](a)
Feet: [A](a: A)scalaz.@@[A,Feet]

scala> val mass = Feet(20.0)
mass: scalaz.@@[Double,Feet] = 20.0

scala> 2 * mass
res2: Double = 40.0

to also add CM

sealed trait CM

def CM[A](a: A): A @@ CM = Tag[A, CM](a)
CM: [A](a: A)scalaz.@@[A,CM]

scala> val mass = CM(20.0)
mass: scalaz.@@[Double,CM] = 20.0

If you want to restrict multiplication to only Feet then you could write a typeclass type multiplication function

trait Multiply[T] { self =>
   def multiply(a: T, b: T): T
}
implicit val footInstance = new Multiply[Feet] {
   def multiply(a: Feet, b: Feet): Feet = Feet(a * b)
}
implicit val cmInstance = new Multiply[CM] {
  def multiply(a: CM, b: CM): CM = CM(a * b)
}

def multiply[T: Multiply](a: T, b: T): T = {
  val multi = implicitly[Multiply[T]]
  multi.multiply(a,b)
} 

you can then do

multiply(Feet(5), Feet(10)) // would return Feet(50)

this is the best Scala can do

To learn more about the boxed type check out http://eed3si9n.com/learning-scalaz-day3

Reuben Doetsch
  • 616
  • 4
  • 10
6

You could use NewType from the scala-newtype library!

Shameless plug: I am the author of scala-newtype

https://github.com/estatico/scala-newtype

This combines the ideas from Scalaz and Shapeless as well as introduces ideas straight from Haskell (like GeneralizedNewTypeDeriving).

Here's what the your code may look like using newtype. We'll give both Feet and Cm their own distinct types and have them derive the Numeric type class based on the one for Double (which deriving does automatically).

We can then use the extension methods provided by Numeric.Implicits -

object Example {

  type Feet = Feet.Type
  object Feet extends NewType.Default[Double] {
    implicit val num: Numeric[Type] = deriving
  }

  type Cm = Cm.Type
  object Cm extends NewType.Default[Double] {
    implicit val num: Numeric[Type] = deriving
  }

  val widthInFeet = Feet(1.0)
  val widthInCm = Cm(30.48)

  import Numeric.Implicits._

  // Does not compile:
  // val nonsense = widthInFeet + widthInCm

  // Compiles!
  val doubleWidthInFeet: Feet = widthInFeet + widthInFeet
}

However, you use * in the example, and we wouldn't want Feet * Feet = Feet as it should really be Feet * Feet = Feet², so let's add a FeetSq type to represent that and define our own operations to be more type safe than Numeric -

object Example {

  type Feet = Feet.Type
  object Feet extends NewType.Default[Double] {
    implicit final class Ops(val self: Feet) extends AnyVal {
      def +(other: Feet) = Feet(self.repr + other.repr)
      def -(other: Feet) = Feet(self.repr - other.repr)
      def *(other: Feet) = FeetSq(self.repr * other.repr)
    }
  }

  type FeetSq = FeetSq.Type
  object FeetSq extends NewType.Default[Double]

  type Cm = Cm.Type
  object Cm extends NewType.Default[Double]

  val widthInFeet = Feet(1.0)
  val widthInCm = Cm(30.48)

  // Does not compile:
  // val nonsense = widthInFeet * widthInCm

  // Compiles!
  val squareFeet: FeetSq = widthInFeet * widthInFeet
}

Here we're using an implicit final class Ops to define methods on our newtype. This class is eliminated at compile time, so at runtime we just end up calling extension methods from the Ops object.

pyrospade
  • 7,870
  • 4
  • 36
  • 52
0

For

val widthInCm: Cm = 30.48

def getWidthInFeet: Feet = widthInCm

, simply defining Feet and Cm like this:

type Feet <: Double
type Cm <: Double

will do the job, as long as you're not up-casting them back to Double. See by yourself:

def getWidthInFeet: Feet = widthInCm

Error:(1, 28) type mismatch;
 found   : widthInCm.type (with underlying type Cm)
 required: Feet
    def getWidthInFeet: Feet = widthInCm

A side effect is that you need to down-cast Double values to either Cm or Feet explicitly in order to get an instance:

val widthInCm: Cm = 30.48.asInstanceOf[Cm]

Also, you can't do any Double operation on them without losing type information (so you would have to down-cast them all the time).

ljleb
  • 182
  • 1
  • 14