scalaphantom-types

Phantom Type w/ AnyVal?


Given the following Phantom Type example in Haskell from phadej:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype Distance a = Distance Double
  deriving (Num, Show)

data Kilometer
data Mile

marathonDistance :: Distance Kilometer
marathonDistance = Distance 42.195

distanceKmToMiles :: Distance Kilometer -> Distance Mile
distanceKmToMiles (Distance km) = Distance (0.621371 * km)

marathonDistanceInMiles :: Distance Mile
marathonDistanceInMiles = distanceKmToMiles marathonDistance

I attempted to translate that to Scala:

case class Distance[A](x: Double) extends AnyVal

case object Kilometer
case object Mile

def marathonDistance: Distance[Kilometer.type] = Distance[Kilometer.type](42.195)

def distanceKmToMiles(kilos: Distance[Kilometer.type]): Distance[Mile.type] = 
    Distance[Mile.type](kilos.x * 0.621371)

def marathonDistanceInMiles: Distance[Mile.type] = distanceKmToMiles( marathonDistance )

Assuming this Phantom Type implementation is valid in Scala, will this usage of Distance result in an allocation, i.e. use the heap, not the stack?

If it will allocate, what's the reason?


Solution

  • As is, your code shouldn't result in any allocation of a Distance class, because it doesn't do any of the things that would cause it:

    1. a value class is treated as another type.
    2. a value class is assigned to an array.
    3. doing runtime type tests, such as pattern matching.

    It doesn't treat them as another type, there are no arrays, and no type tests.

    In these examples, an instance of Distance would be allocated, boxed, and unboxed:

    Type tests:

    def d(dist: Distance[Mile.type]): Double = dist match {
        case Distance(x) if x > 0 => 1.0
        case Distance(x) => x
    }
    

    Treated as another type (generic A):

    def identity[A](t: A): A = t
    

    Arrays:

    val dist = Distance[Meter.type](1.0)
    val arr = Array[Distance[Meter.type]](dist)
    

    The same is true whether the type parameter is there or not.