As far as I can tell, Scala has definitions for the Enumeration Value class for Value(Int), Value(String), and Value(Int, String).
Does anyone know of an example for creating a new Value subclass to support a different constructor?
For example, If I want to create an Enumeration with Value(Int, String, String) objects, how would I do it? I would like all of the other benefits of using the Enumeration class.
Thanks.
The Enumeration values are instance of the Val class. This class can be extended and a factory method can be added.
object My extends Enumeration {
val A = Value("name", "x")
val B = Value("other", "y")
class MyVal(name: String, val x : String) extends Val(nextId, name)
protected final def Value(name: String, x : String): MyVal = new MyVal(name, x)
}
scala> My.B.id
res0: Int = 1
scala> My.B.x
res1: String = y
Actually in Scala Enumeration has a much simpler meaning than in Java. For your purpose you don't have to subclass Enumeration nor its Value in any way, you just need to instantiate your own type in its companion object as a val. This way you'll get the familiar access model of val value:MyEnum = MyEnum.Value as you had in Java which is not possible in the example provided by Thomas Jung. There you'll have def value:My.MyVal = MyEnum.Value which is kinda confusing as it seems to me besides all the hackiness of the solution. Here's an example of what I propose:
class MyEnum(myParam:String)
object MyEnum {
val Value1 = new MyEnum("any parameters you want")
val Value2 = new MyEnum("")
object Value3 extends MyEnum("A different approach to instantialization which also lets you extend the type in place")
}
Here you'll find a more complicated example: Scala Best Practices: Trait Inheritance vs Enumeration
I would prefer doing it by extending the Enumeration.Val class.
For your requirement, I would post a sample below:
object FileType extends Enumeration {
val csv = Val(1,"csv", ",")
val tsv = Val(2,"tsv", "\t")
protected case class Val(num: Int, fileType: String, delimiter: String) extends super.Val
implicit def valueToFileType(x: Value): Val = x.asInstanceOf[Val]
}
Accessing values is as below:
scala> FileType.csv
res0: FileType.Val = csv
scala> FileType.csv.delimiter
res29: String = ,
Here is another simpler approach:
scala> :paste
// Entering paste mode (ctrl-D to finish)
object Colors extends Enumeration {
sealed case class Color private[Colors](hexCode: String, name: String) extends Val(name)
val Black = Color("#000000", "black")
val White = Color("#FFFFFF", "white")
}
// Exiting paste mode, now interpreting.
defined object Colors
scala> Colors.Black.hexCode
res0: String = #000000
scala> Colors.Black.name
res1: String = black
scala> Colors.values
res2: Colors.ValueSet = Colors.ValueSet(black, white)
scala>
Related
Consider the following code:
trait GeneralTrait[T] {
def getClassName() = ???
def params() = ???
}
case class SomeClass(id: Long, name: String)
class ConcreteClass extends GeneralTrait[SomeClass]
Is it possible to make getClassName() return a String 'SomeClass' and to make params() return a list ['id', 'name'] ?
Well... I am not sure about what you are actually trying to do.
But, you can achieve most of this like following,
trait GeneralTrait { self =>
def getClassName() = self.getClass.getSimpleName
def params() = self.getClass.getConstructors.apply(0).getParameters
}
case class SomeClass(id: Long, name: String) extends GeneralTrait
val sc = SomeClass(5, "some")
sc.getClassName()
//res0: String = SomeClass
sc.params()
//res1: Array[java.lang.reflect.Parameter] = Array(final A$A55$A$A55 $outer, final long id, final java.lang.String name)
I would advise you to always use scala reflection instead of java reflection. Names of classes and methods tend to get garbled a bit during compilation. Java doesn't know scala so it will always give you the garbled names.
If you want information about type parameters you will have to use scala reflection anyway.
scala> :paste
// Entering paste mode (ctrl-D to finish)
import scala.reflect.runtime.universe.{TypeTag, typeOf}
trait GeneralTrait[T] {
def getClassName()(implicit tag: TypeTag[T]) = typeOf[T].typeSymbol.name.toString
def params()(implicit tag: TypeTag[T]) = typeOf[T].typeSymbol.asClass.primaryConstructor.asMethod.paramLists.flatten.map(_.name.toString)
}
case class SomeClass(id: Long, name: String)
class ConcreteClass extends GeneralTrait[SomeClass]
// Exiting paste mode, now interpreting.
scala> val c = new ConcreteClass
c: ConcreteClass = ConcreteClass#64d776b6
scala> c.getClassName
res0: String = SomeClass
scala> c.params
res1: List[String] = List(id, name)
say i have enumeration like this
object SimpleEnum extends Enumeration{
val ONE = Value(1)
val TWO = Value(2)
val THREE = Value(3)
etc...
}
also i've a class which i want to be extending Ordered[SimpleEnum]
class SimpleClass(se: SimpleEnum) extends Ordered[SimpleEnum] {
override def compare(that: SimpleEnum): Int = this.se.[here i want to get instance of SimpleEnum, just like i used to do in java, but unfortunately it's not possible]
}
So in SimpleClass i just need to get the Value attached to corresponding SimpleEnum val.
In java i'm able to declare a variable on enum and access an actual value corresponding to it by this var, in scala i'm looking for a way to get an instance of enum and reach out for its value.
It is possible, use id method.
scala> object E extends Enumeration {
val A = Value(1)
val B = Value(7)
val C = Value(2)
}
defined object E
scala> E.A.id
res7: Int = 1
scala> E.B.id
res8: Int = 7
scala> E.C.id
res9: Int = 2
Enumeration values can also be easly compared
scala> E.A < E.B
res10: Boolean = true
scala> E.C < E.B
res11: Boolean = true
scala> E.B < E.A
res12: Boolean = false
Refer to documentation for more
Edit
Your code in the picture is wrong. Firstly, as in your original code (that is not in the picture), SimpleEnum should be an object, not a class. As soon as you make that change your code won't compile and that should ring a bell.
You want SimpleClass to be able to wrap your enum values. Type of those values (i.e. ONE, TWO, THREE) is not SimpleEnum, it is SimpleEnum.Value. Objects of this type have id method.
class SimpleClass(se: SimpleEnum.Value) extends Ordered[SimpleEnum.Value] {
override def compare(that: SimpleEnum.Value): Int = se.id
}
A common thing to do is to declare a type alias for Value with exact same name as the enum object. Then you can import this type and use it
object SimpleEnum extends Enumeration {
type SimpleEnum = Value
val ONE = Value(1)
val TWO = Value(2)
val THREE = Value(3)
}
import SimpleEnum.SimpleEnum
class SimpleClass(se: SimpleEnum) extends Ordered[SimpleEnum] {
override def compare(that: SimpleEnum): Int = se.id
}
Note that Enumeration#Value already implements Ordered[Value]. You can verify it in the docs that I linked earlier.
There is no classic java enum in scala, but because the language is so cool, it was possible to create a regular class called Enumeration that with some tricks allows for similar behavior.
The question is already answered but my approach is the following:
object SimpleEnum extends Enumeration{
type SimpleEnum = Value
val ONE = Value(1)
val TWO = Value(2)
val THREE = Value(3)
etc...
}
class SimpleClass(val i : Int){
def getNumber() {
import SimpleEnum._
SimpleEnum(i)
}
}
I'm using Scala 2.10.2, and have two case classes that have identical fields:
case class Foo(id: String, name: String)
case class Bar(id: String, name: String)
I'd like to do something like this:
case class Thing(id: String, name: String)
type Foo = Thing
type Bar = Thing
This compiles, but when I try to create a Foo, I get:
scala> Bar("a", "b")
<console>:8: error: not found: value Bar
Bar("a", "b")
^
Does type aliasing not work with case classes?
When you create a case class Scala automatically creates a companion object for it. In your code you define an alias for the type Thing, i.e. for the class Thing only. Your companion object Thing still has only 1 name and no aliases.
One way to "fix" it is to create a reference to the companion object (not a type alias) like this:
scala> val Bar = Thing
Bar: Thing.type = Thing
scala> Bar("a", "b")
res1: Thing = Thing(a,b)
Another way to "fix" it would be to rename the imported object with import package.{Thing => Bar}.
Type aliases only alias the type, not any companion object that might be supplying factory methods (whether you write that factory method yourself or get one "for free" from the compiler).
On the other hand, importing acts on names and if there are multiple entities associated with a given name, importing that name brings in every referent of the imported name. Additionally, you can rename when importing and you can do so multiply, so...
scala> object Stuff { case class Thing(id: String, name: String) }
defined module Stuff
scala> import Stuff.Thing
import Stuff.Thing
scala> import Stuff.{Thing => Foo}
import Stuff.{Thing=>Foo}
scala> import Stuff.{Thing => Bar}
import Stuff.{Thing=>Bar}
scala> val thing1 = Thing("fing", "fang")
thing1: Stuff.Thing = Thing(fing,fang)
scala> val foo1 = Foo("yes", "no")
foo1: Stuff.Thing = Thing(yes,no)
scala> val bar1 = Bar("true", "false")
bar1: Stuff.Thing = Thing(true,false)
It's no good for the rendering via toString, though, as you can see.
case class Thing(id: String, name: String)
type Foo = Thing
type Bar = Thing
if you say new Bar("a","b") it will work
Do you want Foo and Bar to be distinguishable (as when they are different case classes) or not? If yes, and you just want to avoid repeating list of fields, you can do this:
case class Foo(thing: Thing)
case class Bar(thing: Thing)
But this will obviously make them a bit less convenient to use in pattern matching or access fields. Field access can be improved a bit:
trait ThingWrapper {
def thing: Thing
def id = thing.id
def name = thing.name
}
case class Foo(thing: Thing) extends ThingWrapper
case class Bar(thing: Thing) extends ThingWrapper
I heard that with Dynamic it is somehow possible to do dynamic typing in Scala. But I can't imagine how that might look like or how it works.
I found out that one can inherit from trait Dynamic
class DynImpl extends Dynamic
The API says that one can use it like this:
foo.method("blah") ~~> foo.applyDynamic("method")("blah")
But when I try it out it doesn't work:
scala> (new DynImpl).method("blah")
<console>:17: error: value applyDynamic is not a member of DynImpl
error after rewriting to new DynImpl().<applyDynamic: error>("method")
possible cause: maybe a wrong Dynamic method signature?
(new DynImpl).method("blah")
^
This is completely logical, because after looking to the sources, it turned out that this trait is completely empty. There is no method applyDynamic defined and I can't imagine how to implement it by myself.
Can someone show me what I need to do to make it to work?
Scalas type Dynamic allows you to call methods on objects that don't exist or in other words it is a replica of "method missing" in dynamic languages.
It is correct, scala.Dynamic doesn't have any members, it is just a marker interface - the concrete implementation is filled-in by the compiler. As for Scalas String Interpolation feature there are well defined rules describing the generated implementation. In fact, one can implement four different methods:
selectDynamic - allows to write field accessors: foo.bar
updateDynamic - allows to write field updates: foo.bar = 0
applyDynamic - allows to call methods with arguments: foo.bar(0)
applyDynamicNamed - allows to call methods with named arguments: foo.bar(f = 0)
To use one of these methods it is enough to write a class that extends Dynamic and to implement the methods there:
class DynImpl extends Dynamic {
// method implementations here
}
Furthermore one need to add a
import scala.language.dynamics
or set the compiler option -language:dynamics because the feature is hidden by default.
selectDynamic
selectDynamic is the easiest one to implement. The compiler translates a call of foo.bar to foo.selectDynamic("bar"), thus it is required that this method has an argument list expecting a String:
class DynImpl extends Dynamic {
def selectDynamic(name: String) = name
}
scala> val d = new DynImpl
d: DynImpl = DynImpl#6040af64
scala> d.foo
res37: String = foo
scala> d.bar
res38: String = bar
scala> d.selectDynamic("foo")
res54: String = foo
As one can see, it is also possible to call the dynamic methods explicitly.
updateDynamic
Because updateDynamic is used to update a value this method needs to return Unit. Furthermore, the name of the field to update and its value are passed to different argument lists by the compiler:
class DynImpl extends Dynamic {
var map = Map.empty[String, Any]
def selectDynamic(name: String) =
map get name getOrElse sys.error("method not found")
def updateDynamic(name: String)(value: Any) {
map += name -> value
}
}
scala> val d = new DynImpl
d: DynImpl = DynImpl#7711a38f
scala> d.foo
java.lang.RuntimeException: method not found
scala> d.foo = 10
d.foo: Any = 10
scala> d.foo
res56: Any = 10
The code works as expected - it is possible to add methods at runtime to the code. On the other side, the code isn't typesafe anymore and if a method is called that doesn't exist this must be handled at runtime as well. In addition this code is not as useful as in dynamic languages because it is not possible to create the methods that should be called at runtime. This means that we can't do something like
val name = "foo"
d.$name
where d.$name would be transformed to d.foo at runtime. But this is not that bad because even in dynamic languages this is a dangerous feature.
Another thing to note here, is that updateDynamic needs to be implemented together with selectDynamic. If we don't do this we will get a compile error - this rule is similar to the implementation of a Setter, which only works if there is a Getter with the same name.
applyDynamic
The ability to call methods with arguments is provided by applyDynamic:
class DynImpl extends Dynamic {
def applyDynamic(name: String)(args: Any*) =
s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"
}
scala> val d = new DynImpl
d: DynImpl = DynImpl#766bd19d
scala> d.ints(1, 2, 3)
res68: String = method 'ints' called with arguments '1', '2', '3'
scala> d.foo()
res69: String = method 'foo' called with arguments ''
scala> d.foo
<console>:19: error: value selectDynamic is not a member of DynImpl
The name of the method and its arguments again are separated to different parameter lists. We can call arbitrary methods with an arbitrary number of arguments if we want but if we want to call a method without any parentheses we need to implement selectDynamic.
Hint: It is also possible to use apply-syntax with applyDynamic:
scala> d(5)
res1: String = method 'apply' called with arguments '5'
applyDynamicNamed
The last available method allows us to name our arguments if we want:
class DynImpl extends Dynamic {
def applyDynamicNamed(name: String)(args: (String, Any)*) =
s"method '$name' called with arguments ${args.mkString("'", "', '", "'")}"
}
scala> val d = new DynImpl
d: DynImpl = DynImpl#123810d1
scala> d.ints(i1 = 1, i2 = 2, 3)
res73: String = method 'ints' called with arguments '(i1,1)', '(i2,2)', '(,3)'
The difference in the method signature is that applyDynamicNamed expects tuples of the form (String, A) where A is an arbitrary type.
All of the above methods have in common that their parameters can be parameterized:
class DynImpl extends Dynamic {
import reflect.runtime.universe._
def applyDynamic[A : TypeTag](name: String)(args: A*): A = name match {
case "sum" if typeOf[A] =:= typeOf[Int] =>
args.asInstanceOf[Seq[Int]].sum.asInstanceOf[A]
case "concat" if typeOf[A] =:= typeOf[String] =>
args.mkString.asInstanceOf[A]
}
}
scala> val d = new DynImpl
d: DynImpl = DynImpl#5d98e533
scala> d.sum(1, 2, 3)
res0: Int = 6
scala> d.concat("a", "b", "c")
res1: String = abc
Luckily, it is also possible to add implicit arguments - if we add a TypeTag context bound we can easily check the types of the arguments. And the best thing is that even the return type is correct - even though we had to add some casts.
But Scala would not be Scala when there is no way to find a way around such flaws. In our case we can use type classes to avoid the casts:
object DynTypes {
sealed abstract class DynType[A] {
def exec(as: A*): A
}
implicit object SumType extends DynType[Int] {
def exec(as: Int*): Int = as.sum
}
implicit object ConcatType extends DynType[String] {
def exec(as: String*): String = as.mkString
}
}
class DynImpl extends Dynamic {
import reflect.runtime.universe._
import DynTypes._
def applyDynamic[A : TypeTag : DynType](name: String)(args: A*): A = name match {
case "sum" if typeOf[A] =:= typeOf[Int] =>
implicitly[DynType[A]].exec(args: _*)
case "concat" if typeOf[A] =:= typeOf[String] =>
implicitly[DynType[A]].exec(args: _*)
}
}
While the implementation doesn't look that nice, its power can't be questioned:
scala> val d = new DynImpl
d: DynImpl = DynImpl#24a519a2
scala> d.sum(1, 2, 3)
res89: Int = 6
scala> d.concat("a", "b", "c")
res90: String = abc
At the top of all, it is also possible to combine Dynamic with macros:
class DynImpl extends Dynamic {
import language.experimental.macros
def applyDynamic[A](name: String)(args: A*): A = macro DynImpl.applyDynamic[A]
}
object DynImpl {
import reflect.macros.Context
import DynTypes._
def applyDynamic[A : c.WeakTypeTag](c: Context)(name: c.Expr[String])(args: c.Expr[A]*) = {
import c.universe._
val Literal(Constant(defName: String)) = name.tree
val res = defName match {
case "sum" if weakTypeOf[A] =:= weakTypeOf[Int] =>
val seq = args map(_.tree) map { case Literal(Constant(c: Int)) => c }
implicitly[DynType[Int]].exec(seq: _*)
case "concat" if weakTypeOf[A] =:= weakTypeOf[String] =>
val seq = args map(_.tree) map { case Literal(Constant(c: String)) => c }
implicitly[DynType[String]].exec(seq: _*)
case _ =>
val seq = args map(_.tree) map { case Literal(Constant(c)) => c }
c.abort(c.enclosingPosition, s"method '$defName' with args ${seq.mkString("'", "', '", "'")} doesn't exist")
}
c.Expr(Literal(Constant(res)))
}
}
scala> val d = new DynImpl
d: DynImpl = DynImpl#c487600
scala> d.sum(1, 2, 3)
res0: Int = 6
scala> d.concat("a", "b", "c")
res1: String = abc
scala> d.noexist("a", "b", "c")
<console>:11: error: method 'noexist' with args 'a', 'b', 'c' doesn't exist
d.noexist("a", "b", "c")
^
Macros give us back all compile time guarantees and while it is not that useful in the above case, maybe it can be very useful for some Scala DSLs.
If you want to get even more information about Dynamic there are some more resources:
The official SIP proposal that introduced Dynamic into Scala
Practical uses of a Dynamic type in Scala - another question on SO (but very outdated)
kiritsuku's answer is better. This is to show a practical TL;DR use case.
Dynamics can be used to dynamically construct an object with the builder pattern.
import scala.language.dynamics
case class DynImpl(
inputParams: Map[String, List[String]] = Map.empty[String, List[String]]
) extends Dynamic {
def applyDynamic(name: String)(args: String*): DynImpl = {
copy(inputParams = inputParams ++ Map(name -> args.toList))
}
}
val d1 = DynImpl().whatever("aaa", "bbb").cool("ccc")
println(d1.inputParams) // Map(whatever -> List(aaa, bbb), cool -> List(ccc))
val d2 = DynImpl().whatever("aaa", "bbb").fun("haha")
println(d2.inputParams) // Map(whatever -> List(aaa, bbb), fun -> List(haha))
How to programmatically determine if the given class is a case class or a simple class?
Using new Scala reflection API:
scala> class B(v: Int)
defined class B
scala> case class A(v: Int)
defined class A
scala> def isCaseClassOrWhat_?(v: Any): Boolean = {
| import reflect.runtime.universe._
| val typeMirror = runtimeMirror(v.getClass.getClassLoader)
| val instanceMirror = typeMirror.reflect(v)
| val symbol = instanceMirror.symbol
| symbol.isCaseClass
| }
isCaseClassOrWhat_$qmark: (v: Any)Boolean
scala> class CaseClassWannabe extends Product with Serializable {
| def canEqual(that: Any): Boolean = ???
| def productArity: Int = ???
| def productElement(n: Int): Any = ???
| }
defined class CaseClassWannabe
scala> isCaseClassOrWhat_?("abc")
res0: Boolean = false
scala> isCaseClassOrWhat_?(1)
res1: Boolean = false
scala> isCaseClassOrWhat_?(new B(123))
res2: Boolean = false
scala> isCaseClassOrWhat_?(A(321))
res3: Boolean = true
scala> isCaseClassOrWhat_?(new CaseClassWannabe)
res4: Boolean = false
Currently (2011), you can use reflection to find out if the class implements the interface scala.Product:
scala> def isCaseClass(o: AnyRef) = o.getClass.getInterfaces.find(_ == classOf[scala.Product]) != None
isCaseClass: (o: AnyRef)Boolean
scala> isCaseClass(Some(1))
res3: Boolean = true
scala> isCaseClass("")
res4: Boolean = false
This is just an approximation - you could go further and check if it has a copy method, if it implements Serializable, if it has a companion object with an appropriate apply or unapply method - in essence, check for all the things expected from a case class using reflection.
The scala reflection package coming in one of the next releases should make case class detection easier and more precise.
EDIT:
You can now do it using the new Scala Reflection library -- see other answer.
If you mean: Can I determine whether a class is a case class or a non-case class programmatically, the answer is no, but you can do an approximation. Case classes are just a compiler hack, they tell the compiler to create certain methods etc. In the final bytecode, there is no difference between normal classes and case classes.
From How does a case class differ from a normal class?
You can do pattern matching on it,
You can construct instances of these classes without using the new
keyword,
All constructor arguments are accessible from outside using
automatically generated accessor functions,
The toString method is automatically redefined to print the name
of the case class and all its arguments,
The equals method is automatically redefined to compare two
instances of the same case class structurally rather than by identity.
The hashCode method is automatically redefined to use the
hashCodes of constructor arguments.
So you can actually create a case class by just defining the correct methods & companion objects yourself.
For an idea as to how to find out if a class could be a case class, look at the answer from axel22.