Dentro de Scala, ¿es posible alias un tipo pero no permitir el uso cruzado de tipos alias/sin alias como Haskell?
types (2)
Sí, estás usando algo conocido como Tipos Tagged Unboxed en Scala.
Así es como se define Tagged:
type Tagged[U] = { type Tag = U }
type @@[T, U] = T with Tagged[U]
Esto te permite hacer algo como esto
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
para agregar también 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
Si quieres restringir la multiplicación a solo Pies, entonces puedes escribir una función de multiplicación tipo tipo de letra
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)
}
entonces puedes hacer
multiply(Feet(5), Feet(10)) // would return Feet(50)
esto es lo mejor que Scala puede hacer
Para obtener más información sobre el tipo de caja, echa un vistazo a http://eed3si9n.com/learning-scalaz-day3
En Haskell, creo que es posible aliar un tipo de tal manera que el compilador no permite referencias entre el tipo con alias y el tipo no alias. De acuerdo con esta pregunta de desbordamiento de pila , uno puede usar el newtype
tipo de Haskell así:
newtype Feet = Feet Double
newtype Cm = Cm Double
donde Feet
y Cm
se comportarán como valores dobles, pero intentar multiplicar un valor de Feet
y un valor de Cm
generará un error de compilación.
EDITAR: Ben señaló en los comentarios que esta definición anterior en Haskell es insuficiente. Feet
y Cm
serán nuevos tipos, en los cuales no habrá funciones definidas. Investigando un poco más, descubrí que lo siguiente funcionará:
newtype Feet = Feet Double deriving (Num)
newtype Cm = Cm Double deriving (Num)
Esto crea un nuevo tipo que deriva del tipo Num
existente (requiere el uso de switch: -XGeneralizedNewtypeDeriving
). Por supuesto, estos nuevos tipos serán aún más valiosos derivados de otros tipos, como Show
, Eq
, etc., pero este es el mínimo requerido para evaluar correctamente Cm 7 * Cm 9
.
Tanto Haskell como Scala tienen type
, que simplemente alias un tipo existente y permite un código sin sentido como este ejemplo en 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
¿Scala tiene un newtype
equivalente, suponiendo que esto hace lo que creo que hace?
Otra opción sería usar clases de valor. Estos crean un contenedor alrededor de un tipo subyacente que se convierte en acceso directo al tipo sin procesar en tiempo de compilación, con métodos en la clase que se convierten en llamadas estáticas en un objeto complementario asociado. Por ejemplo:
class CM(val quant : Double) extends AnyVal {
def +(b : CM) = new CM(quant + b.quant)
def *(b : Int) = new CM(quant * b)
}