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?
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)
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With