scala shapeless

scala - Pasar un registro extensible sin forma a una función



shapeless (2)

A partir de la versión sin formato 2.1.0 hay una nueva sintaxis para expresar los tipos de registro:

scala> :paste // Entering paste mode (ctrl-D to finish) import shapeless._ import shapeless.record._ import shapeless.syntax.singleton._ def fun(x: Record.`"foo" -> Int`.T) = x("foo") // Exiting paste mode, now interpreting. import shapeless._ import shapeless.record._ import shapeless.syntax.singleton._ fun: (x: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil])Int scala> fun( ("foo" ->> 42) :: HNil ) res2: Int = 42 scala> fun( ("foo" ->> 42) :: ("bar" ->> 43) :: HNil ) <console>:30: error: type mismatch; found : shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.::[Int with shapeless.labelled.KeyTag[String("bar"),Int],shapeless.HNil]] required: shapeless.::[Int with shapeless.labelled.KeyTag[String("foo"),Int],shapeless.HNil] fun( ("foo" ->> 42) :: ("bar" ->> 43) :: HNil )

Pero el Selector es probablemente el mejor enfoque para el caso de uso de OP.

Estoy tratando de aprender Shapeless (usando la versión 2.10.2). He creado un registro extensible muy simple:

val rec1 = ("foo" ->> 42) :: HNil

Según el REPL, este tiene tipo

shapeless.::[Int with shapeless.record.KeyTag[String("foo"),Int],shapeless.HNil]

Estoy tratando de definir una función simple:

def fun(x: ::[Int with KeyTag[String("foo"), Int], HNil]) = x("foo")

Pero ni siquiera compila. No puedo usar un String ("foo") en la declaración de tipo y obtengo un error.

Tengo dos preguntas:

  1. ¿Cómo puedo especificar el tipo de registro extensible en mi código?
  2. Cuando trabaje con registros con más campos, la longitud y complejidad de la declaración de tipo será inmanejable. ¿Hay una manera de crear un alias para el tipo, dada una instancia particular de un registro, o alguna otra solución?

EDITAR

He encontrado que

val rec1 = ("foo" ->> 42) :: HNil val rec2 = ("foo" ->> 43) :: HNil var x = rec1 x = rec2

funciona bien. Concluyo que rec1, rec2 y x son del mismo tipo. ¡Simplemente no sé cómo expresar ese tipo de código!


Aquí hay algo un poco más general que creo que podría responder a su pregunta. Supongamos que queremos escribir un método que funcione en cualquier registro con una tecla "foo" . Podemos usar una combinación de un testigo y un selector:

import shapeless._, record._, syntax.singleton._ val w = Witness("foo") def fun[L <: HList](xs: L)(implicit sel: ops.record.Selector[L, w.T]) = xs("foo")

Y entonces:

scala> fun(("foo" ->> 42) :: HNil) res0: Int = 42

O:

scala> fun(("bar" ->> ''a) :: ("foo" ->> 42) :: HNil) res1: Int = 42

Si realmente quisiéramos permitir solo registros sin otros campos, podríamos escribir lo siguiente:

def fun(l: Int with KeyTag[w.T, Int] :: HNil) = l("foo")

Pero eso es algo en desacuerdo con la forma en que se utilizan generalmente los registros.

Tenemos que definir el testigo precisamente porque Scala 2.10 no proporciona ninguna manera de referirse a un tipo de singleton directamente; vea, por ejemplo, mi tenedor del proyecto Shona de Alois Cochard para una discusión.

Agregaré como descargo de responsabilidad final que acabo de familiarizarme con Shapeless 2.0, pero no creo que incluso Miles sea lo suficientemente mágico como para sortear esta limitación.