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)
Related
Suppose I have a set of converters to String, as a Type class:
import scala.reflect.runtime.universe._
abstract class ToStringConverter[T] {
def convert(value: T): String
}
implicit object IntToStringConverter extends ToStringConverter[Int] {
def convert(value: Int) = value.toString
}
implicit object DoubleStringConverter extends ToStringConverter[Double] {
def convert(value: Double) = value.toString
}
and a convert method that uses the type information to pick right converter:
def convert[T](v: T)(implicit ev: ToStringConverter[T]): String = ev.convert(v)
This works fine If I have the concrete type in advance, for example:
scala> convert[Double](12.2)
res0: String = 12.2
scala> convert[Int](12)
res1: String = 12
Is it possible to use the convert method above with a runtime type, for example, with a type 't' below?
scala> val t = typeOf[Double]
t: reflect.runtime.universe.Type = Double
If you want to do the resolution runtime, reflection is needed, as implicits are resolved compile time. A code like this should do the job:
import scala.reflect.runtime.universe._
abstract class ToStringConverterAny {
def convertAny(value: Any): String
}
abstract class ToStringConverter[T] extends ToStringConverterAny {
def convertAny(value: Any): String = convert(value.asInstanceOf[T])
def convert(value: T): String
}
implicit object IntToStringConverter extends ToStringConverter[Int] {
def convert(value: Int) = value.toString
}
implicit object DoubleStringConverter extends ToStringConverter[Double] {
def convert(value: Double) = value.toString
}
val converters: Map[Type, ToStringConverterAny] = Map(
typeOf[Int] -> IntToStringConverter,
typeOf[Double] -> DoubleStringConverter
)
def convert(t: Type, v: Any) = {
converters(t).convertAny(v)
}
def convert[T](v: T)(implicit ev: ToStringConverter[T]): String = ev.convert(v)
convert[Double](12.2)
convert[Int](12)
val t = typeOf[Double]
val v: Any = 1.23
convert(t, v)
If you want to building converters map automatically, you could also use reflection for this, but enumerating derived classes requires surprisingly non-trivial code (including class loaders - which is understandable when you think about it).
If you can make the ToStringConverterAny sealed, enumerating over its subclasses in a macro should be a bit easier.
I'm trying to write some string utils to be able to do implicit conversions of the form "myString".doSpecialConversion and have a specialValue based on which doSpecialConversion works.
Attempt-1: Use a trait:
trait ConversionUtils {
// Need an overridable value
lazy val specialValue = ","
implicit class StringConversions(val s: String) {
def doSpecialConversion: Option[String] = if (s == specialValue) None else Some(s)
}
}
Trait works just fine but the problem is that its not static so multiple StringConversions will be created which is undesired. So I try to extend AnyVal which can't be done for a trait because Another limitation that is a result of supporting only one parameter to a class is that a value class must be top-level or a member of a statically accessible object.
Attempt-2: Use a singleton:
object ConversionUtils {
// Need an overridable value
lazy val specialValue = ","
implicit class StringConversions(val s: String) extends AnyVal {
def doSpecialConversion: Option[String] = if (s == specialValue) None else Some(s)
}
}
Question: How do I provide a Util to be able to override the specialValue for StringConversions and be true-ly static?
You can ask for an implicit parameter:
object ConversionUtils {
case class SpecialValue(str: String)
implicit class StringConversions(val s: String) extends AnyVal {
def doSpecialConversion(implicit sv: SpecialValue): Option[String] = if (s == sv.str) None else Some(s)
}
}
Usage:
scala> implicit val sp = SpecialValue(",")
sp: ConversionUtils.SpecialValue = SpecialValue(,)
scala> "aaa".doSpecialConversion
res0: Option[String] = Some(aaa)
scala> ",".doSpecialConversion
res1: Option[String] = None
In general case, macro-libraries, like machinist might help to get rid of boilerplate.
I have two classes with some data members like this:
class MyInfo {
private val myId: String
private val time: DateTime
private val solution: Long
private val banner: Int
}
Class HisInfo {
private val id: String
private val solution: Long
private val banner: Int
}
As you can see these two classes share two members, and in my real project they share more. I need to save them into hbase and designed a class like this:
sealed trait Element[T] {
def serialize(value: T)(implicit val helper: Helper[T]): Array[Byte]
def deserialize(bytes: Array[Byte])(implicit val helper: Helper[T]): T
}
case object LongElement extends Element[Long] {...}
case object IntElement extends Element[Int] {...}
class Info {
protected val data: Map[Element[_], Any] = new mutable.Map[Element[_], Any]()
}
class MyInfo extends Info {
val elements = List(LongElement, IntLement)
def saveToHBase = {
elements.foreach { e =>
val v = e.serialize(data(e))
// do sth with v
}
}
In fact I have defined implementations of Helper[Int] and Helper[Long], but the compiler complains that it can not find implicit value for parameter Helper[_1]. Can someone help me to design these classes?
In fact I have defined implementations of Helper[Int] and Helper[Long], but the compiler complains that it can not find implicit value for parameter Helper[_1].
Well, consider what would happen if elements included an Element[String] (or some other type for which you have no implicit Helper). Given the type of elements, the compiler can't know it doesn't.
I think that if you need a Helper for all methods of Element anyway, you should just make it a part of the type:
sealed trait Element[T] {
val helper: Helper[T] // or def if you want to put it in the companion object
def serialize(value: T): Array[Byte]
def deserialize(bytes: Array[Byte]): T
}
or
sealed abstract class Element[T](implicit val helper: Helper[T]) {
def serialize(value: T): Array[Byte]
def deserialize(bytes: Array[Byte]): T
}
At least in most situations.
It definitely looks like work for shapeless
Cool thing about shapeless, it can convert your class to list like structure named HList.
Scala ordinary collections like List should drop information about elements to bring them to common type.
HList could save type of each elements, while providing List-like functionality
Lets define your type with their common fields in separate type
import org.joda.time.DateTime
class Common(
val solution: Long,
val banner: Int
)
class MyInfo(
myId: String,
time: DateTime,
solution: Long,
banner: Int
) extends Common(solution, banner)
class HistInfo(
id: String,
solution: Long,
banner: Int
) extends Common(solution, banner)
Now lets define something looks like serialization:
trait Helper[T] extends (T => Array[Byte])
implicit object longHelper extends Helper[Long] {
def apply(x: Long) = 0 to 7 map (i => (x >> (i * 8)).toByte) toArray
}
implicit object intHelper extends Helper[Int] {
def apply(x: Int) = 0 to 3 map (i => (x >> (i * 8)).toByte) toArray
}
Now something interesting. We'll create special object that could convert your Common type to special HList which contains every value preserving it's type information and statically-saved string with field name:
import shapeless._
val lgen = LabelledGeneric[Common]
Next we define special function-like thing to map over such HList. It would find known implicit Helper and tie its result with corresponding field name:
import shapeless.labelled.FieldType
object serialized extends Poly1 {
implicit def serialize[K <: Symbol, T]
(implicit helper: Helper[T], key: Witness.Aux[K])
= at[FieldType[K, T]](field => key.value.name -> helper(field))
}
Now we define some user of this function:
def extractMap(x: Common): Map[String, Seq[Byte]] =
lgen.to(histInfo).map(serialized).toList.toMap.mapValues(_.toSeq)
You could verify you function is working:
val histInfo = new HistInfo("123", 12, 3)
println(extractMap(histInfo))
will print
Map(solution -> WrappedArray(12, 0, 0, 0, 0, 0, 0, 0), banner ->
WrappedArray(3, 0, 0, 0))
I'm aware that case class inheritance is deprecated in Scala, but for the sake of simplicity, I've used it in the following example:
scala> case class Foo(val f: String) { def foo(g: String): Foo = { this.copy(f=g) }}
defined class Foo
scala> case class Bar(override val f: String) extends Foo(f)
warning: there were 1 deprecation warnings; re-run with -deprecation for details
defined class Bar
scala> Bar("F")
res0: Bar = Foo(F)
scala> res0.foo("G")
res1: Foo = Foo(G)
So far, so good. What I really want, though, is to be able to write a method foo() in Foo that returns an object of type Bar when called on an object of type Bar, without having to reimplement the method in class Bar. Is there a way to do this in Scala?
builder approach
Yes, it can be done. A good example of that is the collections library.
scala> List(1, 2, 3) take 2
res1: List[Int] = List(1, 2)
scala> Array(1, 2, 3) take 2
res2: Array[Int] = Array(1, 2)
See The Architecture of Scala Collections to see how it was done.
Edit:
It uses two approaches to reuse implementations. The first is by using common traits and builders, and the other is using type classes.
scala> :paste
// Entering paste mode (ctrl-D to finish)
trait Builder[A] {
def apply(f: String): A
}
trait FooLike[A] {
def builder: Builder[A]
def f: String
def genericCopy(f: String): A = builder(f)
def map(fun: String => String): A = builder(fun(f))
}
case class Foo(f: String) extends FooLike[Foo] {
def builder = new Builder[Foo] {
def apply(f: String): Foo = Foo(f)
}
}
case class Bar(f: String) extends FooLike[Bar] {
def builder = new Builder[Bar] {
def apply(f: String): Bar = Bar(f)
}
}
scala> Foo("foo").genericCopy("something")
res0: Foo = Foo(something)
scala> Bar("bar").genericCopy("something")
res1: Bar = Bar(something)
scala> Foo("foo") map { _ + "!" }
res2: Foo = Foo(foo!)
The whole point of doing this, is so you can do something interesting at the common trait, like implementing common map in FooLike. It's hard to see the benefits with trivial code.
type class approach
The benefit of using a type class is that you can add features to Foo and Bar even when you can't change them (like String).
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Foo(f: String)
case class Bar(f: String)
trait CanCopy[A] {
def apply(self: A, f: String): A
def f(self: A): String
}
object CanCopy {
implicit val fooCanCopy = new CanCopy[Foo] {
def apply(v: Foo, f: String): Foo = v.copy(f = f)
def f(v: Foo) = v.f
}
implicit val barCanCopy = new CanCopy[Bar] {
def apply(v: Bar, f: String): Bar = v.copy(f = f)
def f(v: Bar) = v.f
}
implicit val stringCanCopy = new CanCopy[String] {
def apply(v: String, f: String): String = f
def f(v: String) = v
}
def copy[A : CanCopy](v: A, f: String) = {
val can = implicitly[CanCopy[A]]
can(v, f)
}
def f[A : CanCopy](v: A) = implicitly[CanCopy[A]].f(v)
}
scala> CanCopy.copy(Foo("foo"), "something")
res1: Foo = Foo(something)
scala> CanCopy.f(Foo("foo"))
res2: String = foo
scala> CanCopy.copy(Bar("bar"), "something")
res3: Bar = Bar(something)
scala> CanCopy.copy("string", "something")
res4: java.lang.String = something
The copy method is implemented by the compiler and it does not seem to belong a common trait. The easiest way to do it is to define a trait:
trait HasFoo[T] {
def foo(g:String): T
}
case class Foo( f: String ) extends HasFoo[Foo] {
def foo( g: String ) = copy(f=g)
}
case class Bar( f: String ) extends HasFoo[Bar] {
def foo( g: String ) = copy(f=g)
}
scala> Bar("a").foo("b")
res7: Bar = Bar(b)
scala> Foo("a").foo("b")
res8: Foo = Foo(b)
Another option is to use type classes to provide an appropriate builder. But it wont save the number of typed characters.
Note: This does not create a new object but re-uses the this object. For general use, see paradigmatic’s answer.
For some reason, it does not work together with the case class’s copy method. (But admittedly, since case class inheritance should not be done anyway, the problem does not occur.). But for any other method, you do it with this.type.
case class Foo(val f: String) { def foo(g: String): this.type = { this }}
case class Bar(override val f: String) extends Foo(f)
Bar("F").foo("G")
res: Bar = Foo(F)
If you need the self-type variance in method arguments and method bodys (as opposed to return-type-only variance), you will need to go one step further and define
trait HasFoo[T <: HasFoo[T]] { this: T =>
def foo(g:String): T
def bar(g: T): T // here may follow an implementation
}
This will allow you to add proper method bodies to the trait. (See: proper class hierarchy for 2D and 3D vectors)
This solution doesn't require a separate trait.
class Bar
class Foo {
def returnMyType[A](x:A) :A = { println(x); x }
}
val f = new Foo
val b = new Bar
val bReturned = f.returnMyType(b)
println(bReturned.getClass.getName)
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>