scala enumeration scala-macros sealed

¿Iteración sobre un rasgo sellado en Scala?



enumeration scala-macros (6)

Algo que también puede resolver el problema es la posibilidad de agregar una conversión implícita para agregar métodos a la enumeración, en lugar de iterar sobre el rasgo sellado.

object SharingPermission extends Enumeration { val READ = Value("READ") val WRITE = Value("WRITE") val MANAGE = Value("MANAGE") } /** * Permits to extend the enum definition and provide a mapping betweet SharingPermission and ActionType * @param permission */ class SharingPermissionExtended(permission: SharingPermission.Value) { val allowRead: Boolean = permission match { case SharingPermission.READ => true case SharingPermission.WRITE => true case SharingPermission.MANAGE => true } val allowWrite: Boolean = permission match { case SharingPermission.READ => false case SharingPermission.WRITE => true case SharingPermission.MANAGE => true } val allowManage: Boolean = permission match { case SharingPermission.READ => false case SharingPermission.WRITE => false case SharingPermission.MANAGE => true } def allowAction(actionType: ActionType.Value): Boolean = actionType match { case ActionType.READ => allowRead case ActionType.WRITE => allowWrite case ActionType.MANAGE => allowManage } } object SharingPermissionExtended { implicit def conversion(perm: SharingPermission.Value): SharingPermissionExtended = new SharingPermissionExtended(perm) }

Solo quería saber si es posible iterar sobre un rasgo sellado en Scala. Si no, ¿por qué no es posible? Dado que el rasgo está sellado, debería ser posible no?

Lo que quiero hacer es algo así:

sealed trait ResizedImageKey { /** * Get the dimensions to use on the resized image associated with this key */ def getDimension(originalDimension: Dimension): Dimension } case class Dimension(width: Int, height: Int) case object Large extends ResizedImageKey { def getDimension(originalDimension: Dimension) = Dimension(1000,1000) } case object Medium extends ResizedImageKey{ def getDimension(originalDimension: Dimension) = Dimension(500,500) } case object Small extends ResizedImageKey{ def getDimension(originalDimension: Dimension) = Dimension(100,100) }

Lo que quiero se puede hacer en Java dando una implementación a los valores enum. ¿Hay un equivalente en Scala?


Eche un vistazo a la question de @TravisBrown A partir del informe 2.1.0-SNAPSHOT, el código publicado en su pregunta funciona y produce un Set de los elementos ADT enumerados que luego pueden atravesarse. Recapitularé su solución aquí para facilitar la referencia ( fetchAll es algo así como mío :-))

import shapeless._ trait AllSingletons[A, C <: Coproduct] { def values: List[A] } object AllSingletons { implicit def cnilSingletons[A]: AllSingletons[A, CNil] = new AllSingletons[A, CNil] { def values = Nil } implicit def coproductSingletons[A, H <: A, T <: Coproduct](implicit tsc: AllSingletons[A, T], witness: Witness.Aux[H] ): AllSingletons[A, H :+: T] = new AllSingletons[A, H :+: T] { def values: List[A] = witness.value :: tsc.values } } trait EnumerableAdt[A] { def values: Set[A] } object EnumerableAdt { implicit def fromAllSingletons[A, C <: Coproduct](implicit gen: Generic.Aux[A, C], singletons: AllSingletons[A, C] ): EnumerableAdt[A] = new EnumerableAdt[A] { def values: Set[A] = singletons.values.toSet } } def fetchAll[T](implicit ev: EnumerableAdt[T]):Set[T] = ev.values


En mi opinión, este es un caso de uso apropiado para las macros 2.10: quieres acceso a la información que sabes que tiene el compilador, pero que no expone, y las macros te dan una manera (razonablemente) fácil de echar un vistazo. Vea mi respuesta here para un ejemplo relacionado (pero ahora un poco desactualizado), o simplemente use algo como esto:

import language.experimental.macros import scala.reflect.macros.Context object SealedExample { def values[A]: Set[A] = macro values_impl[A] def values_impl[A: c.WeakTypeTag](c: Context) = { import c.universe._ val symbol = weakTypeOf[A].typeSymbol if (!symbol.isClass) c.abort( c.enclosingPosition, "Can only enumerate values of a sealed trait or class." ) else if (!symbol.asClass.isSealed) c.abort( c.enclosingPosition, "Can only enumerate values of a sealed trait or class." ) else { val children = symbol.asClass.knownDirectSubclasses.toList if (!children.forall(_.isModuleClass)) c.abort( c.enclosingPosition, "All children must be objects." ) else c.Expr[Set[A]] { def sourceModuleRef(sym: Symbol) = Ident( sym.asInstanceOf[ scala.reflect.internal.Symbols#Symbol ].sourceModule.asInstanceOf[Symbol] ) Apply( Select( reify(Set).tree, newTermName("apply") ), children.map(sourceModuleRef(_)) ) } } } }

Ahora podemos escribir lo siguiente:

scala> val keys: Set[ResizedImageKey] = SealedExample.values[ResizedImageKey] keys: Set[ResizedImageKey] = Set(Large, Medium, Small)

Y todo esto es perfectamente seguro: obtendrá un error en tiempo de compilación si solicita valores de un tipo que no está sellado, tiene hijos no objeto, etc.


La solución mencionada anteriormente basada en Scala Macros funciona muy bien. Sin embargo, no es el caso de:

sealed trait ImageSize object ImageSize { case object Small extends ImageSize case object Medium extends ImageSize case object Large extends ImageSize val values = SealedTraitValues.values[ImageSize] }

Para permitir esto, uno puede usar este código:

import language.experimental.macros import scala.reflect.macros.Context object SealedExample { def values[A]: Set[A] = macro values_impl[A] def values_impl[A: c.WeakTypeTag](c: Context) = { import c.universe._ val symbol = weakTypeOf[A].typeSymbol if (!symbol.isClass) c.abort( c.enclosingPosition, "Can only enumerate values of a sealed trait or class." ) else if (!symbol.asClass.isSealed) c.abort( c.enclosingPosition, "Can only enumerate values of a sealed trait or class." ) else { val siblingSubclasses: List[Symbol] = scala.util.Try { val enclosingModule = c.enclosingClass.asInstanceOf[ModuleDef] enclosingModule.impl.body.filter { x => scala.util.Try(x.symbol.asModule.moduleClass.asClass.baseClasses.contains(symbol)) .getOrElse(false) }.map(_.symbol) } getOrElse { Nil } val children = symbol.asClass.knownDirectSubclasses.toList ::: siblingSubclasses if (!children.forall(x => x.isModuleClass || x.isModule)) c.abort( c.enclosingPosition, "All children must be objects." ) else c.Expr[Set[A]] { def sourceModuleRef(sym: Symbol) = Ident( if (sym.isModule) sym else sym.asInstanceOf[ scala.reflect.internal.Symbols#Symbol ].sourceModule.asInstanceOf[Symbol] ) Apply( Select( reify(Set).tree, newTermName("apply") ), children.map(sourceModuleRef(_)) ) } } } }


No hay capacidad para esto de forma nativa. No tendría sentido en el caso más común, donde en lugar de objetos de caso, usted tenía clases reales como subclase de su rasgo sellado. Parece que su caso podría ser mejor manejado por una enumeración

object ResizedImageKey extends Enumeration { type ResizedImageKey = Value val Small, Medium, Large = Value def getDimension(value:ResizedImageKey):Dimension = value match{ case Small => Dimension(100, 100) case Medium => Dimension(500, 500) case Large => Dimension(1000, 1000) } println(ResizedImageKey.values.mkString(",") //prints Small,Medium,Large

Alternativamente, puede crear una enumeración por su cuenta, posiblemente colocándola en el objeto complementario para mayor comodidad

object ResizedImageKey{ val values = Vector(Small, Medium, Large) } println(ResizedImageKey.values.mkString(",") //prints Small,Medium,Large