2010-10-20 12 views
5

Supongamos que quiero tener una clase como Java Date. Su único miembro de datos es mucho lo que representa los milisegundos desde 1970.Sobrecarga de tipos primitivos de "boxeo" a través de implicits en Scala

¿Sería/Podría ser de cualquier beneficio de rendimiento de sólo hacer un nuevo tipo Scala:

type PrimitiveDate = Long 

continuación, puede agregar mediante el uso de métodos implícitos conversión, como se hace para int con RichInt. ¿Este "boxeo" del tipo primitivo en una clase rica implica algún gasto (creación de clase)? Básicamente sólo podría tener un método estático

def addMonth(date: PrimitiveDate, months: Int): PrimitiveDate = date + 2592000000 * months 

y dejar que la cifra sistema del tipo que tiene que ser aplicada cuando d addMonth 5 aparece dentro de su código.

Editar

Parece que el alias se crea escribiendo type PrimitiveDate = Long no se hace cumplir por el compilador Scala. ¿Está creando una clase adecuada, incluyendo el Long, la única manera de crear un tipo forzado en Scala?

¿Qué tan útil considera que sea capaz de crear un alias de tipo forzado para tipos primitivos?

Respuesta

10

Bueno, escape analysis debe significar que las JVM más recientes en realidad no tienen que crear su rica envoltura con el fin de llamar al método addMonth.

El grado en que esto ocurre realidad en la práctica, obviamente, dependerá de la cantidad de un punto de acceso en tiempo de ejecuciónla JVM decide que estos métodos están agregando en la creación de objetos. Cuando el análisis de escape no está ocurriendo, obviamente la JVM tendrá que "boxear" (como dices) el Long en una nueva instancia de la clase contenedora. No involucra "creación de clase", implicaría "crear una instancia de una clase". Este ejemplo, se corta vida sería entonces GC-d de inmediato, por lo que la cabeza (aunque pequeña) es:

  • asignación de memoria para la instancia
  • GC-ing la instancia

Estos Obviamente, solo habrá algún tipo de problema si está escribiendo un código de latencia muy baja en el que intenta minimizar la creación de basura (en un círculo cerrado). Solo tú sabes si este es el caso.

En cuanto a si el enfoque funcionará para usted (y el análisis de escape viene en su ayuda), tendría que realizar pruebas en la naturaleza. Los micro-puntos de referencia son notoriamente difíciles de escribir para este tipo de cosas.


La razón por la que no llegan a estos como alias de tipo que forman parte de una API pública es que Scala en realidad no hacerlas cumplir tan estrictamente como me gustaría.Por ejemplo:

type PrimitiveDate = Long 
type PrimitiveSpeed = Long 
type Car = String 
type Meeting = String 

var maxSpeeds : Map[Car, PrimitiveSpeed] = Map.empty 

//OOPS - much too easy to accidentally send the wrong type 
def setDate(meeting : Meeting, date : PrimitiveDate) = maxSpeeds += (meeting -> date) 
+0

Buen punto en los "alias obligatorios". Esa era una de las cosas que me preguntaba. Entonces, ¿no hay forma de crear un alias de tipo apropiado para un tipo primitivo en Scala? – ziggystar

+1

@ziggystar - no, no hay. Tiendo a usar alias de tipo para una de dos cosas: cosas privadas internas como las que Kevin habla a continuación; importación de objetos de paquete (es decir, por qué 'List' todavía parece estar en el paquete' scala') –

+0

Creo que Miles Sabin creó una [solución] elegante y de alto rendimiento (https://gist.github.com/milessabin/89c9b47a91017973a35f) 11 meses después de que se hizo esta pregunta. – itsbruce

4

No ha creado realmente un nuevo tipo en el ejemplo dado, es simplemente un alias para el tipo Long preexistente.

Es una técnica que uso bastante a menudo para tratar con conexiones anidadas poco manejables. Por ejemplo, quisiera alias type Grid = Seq[Seq[Int]] para evitar tener que especificar Seq[Seq[Int]] una y otra vez para varios parámetros.

Puede felizmente pasar una Long a un método de tomar un método PrimitiveDate, aunque hacer tienen la ventaja de que el código es mucho mejor auto-documentado.

Si realmente desea crear un nuevo tipo con forzada seguridad de tipos y conveniente coincidencia de patrones, que haría uso de una clase de caso:

case class PrimitiveDate(value:Long) 

y, potencialmente, incluso proporcionar una implícita largo => PrimitiveDate conversión por conveniencia.

3

11 meses después de haber formulado esta pregunta, Miles Sabin descubrió una forma muy simple, elegante y performante de crear unboxed newtypes en Scala. A diferencia de los alias de tipo, las etiquetas de tipo se aplican. Los tipos primitivos necesitan una repetición mínima (una línea por primitiva) para proporcionar especialización.

Un año después, agregó un more polished and robust version de esto a Shapeless. El concepto es lo suficientemente simple y conciso para duplicarse en un proyecto sin agregar Shapeless si no desea el resto de esa excelente biblioteca.

Por supuesto, tanto usted como las personas que respondieron su pregunta probablemente lo sepan, pero vale la pena agregar aquí porque esta sigue siendo una pregunta importante.

+0

Supongo que estos nuevos tipos no compartidos son similares a los tipos fantasmas mencionados [aquí] (http://stackoverflow.com/questions/6358651/marking-primitive-types-with-phantom-types-in-scala/6360260#6360260), ¿derecho? – ziggystar

Cuestiones relacionadas