Create a companion object that mixes in a trait that defines a method which returns an object of the object's companion class - scala

Abstract problem: Create a trait that can be mixed into the companion object of a class, to give that object a method that returns an object of that class.
Concrete problem: I'm trying to create a bunch of classes for use with RESTful service calls, that know how to serialize and de-serialize themselves, like so:
case class Foo
(
var bar : String,
var blip : String
)
extends SerializeToJson
object Foo extends DeserializeFromJson
The intended usage is like so:
var f = Foo( "abc","123" )
var json = f.json
var newF = Foo.fromJson( json )
I'm using Genson to do the serialization/deserialization, which I access through a global object:
object JSON {
val parser = new ScalaGenson( new GensonBuilder() <...> )
}
Then I define the traits like so:
trait SerializeToJson {
def json : String = JSON.parser.toJson(this)
}
trait DeserializeFromJson[T <: DeserializeFromJson[T]] {
def fromJson( json : String ) : T = JSON.parser.fromJson( json )
}
This compiles. But this does not:
object Foo extends DeserializeFromJson[Foo]
I get the following error message:
type arguments [Foo] do not conform to trait DeserializeFromJson's
type parameter bounds [T <: DeserializeFromJson[T]]
I've tried creating a single trait, like so:
trait JsonSerialization[T <: JsonSerialization[T]] {
def json(implicit m: Manifest[JsonSerialization[T]]) : String =
JSON.parser.toJson(this)(m)
def fromJson( json : String ) : T =
JSON.parser.fromJson(json)
}
Now, if I just declare case class Foo (...) extends JsonSerialization[Foo] then I can't call Foo.fromJson because only an instance of class Foo has that method, not the companion object.
If I declare object Foo extend JsonSerialization[Foo] then I can compile and Foo has a .fromJson method. But at run time, the call to fromJson thinks that T is a JsonSerialization, and not a Foo, or so the following run-time error suggests:
java.lang.ClassCastException: scala.collection.immutable.HashMap$HashTrieMap cannot be cast to ...JsonSerialization
at ...JsonSerialization$class.fromJson(DataModel.scala:14)
at ...Foo.fromJson(Foo.scala:6)
And I can't declare object Foo extends Foo because I get
module extending its companion class cannot use default constructor arguments
So I can try adding constructor parameters, and that compiles and runs, but again the run-time type when it tries to deserialize is wrong, giving me the above error.
The only thing I've been able to do that works is to define fromJson in every companion object. But there MUST be a way to define it in a trait, and just mix in that trait. Right?

The solution is to simplify the type parameter for the trait.
trait DeserializeFromJson[T] {
def fromJson( json : String )(implicit m : Manifest[T]) : T =
JSON.parser.fromJson[T](json)(m)
}
Now, the companion object can extend DeserializeFromJson[Foo] and when I call Foo.fromJson( json ) it is able to tell Genson the correct type information so that an object of the appropriate type is created.

The problem is related to how implicits work.
Genson expects a Manifest that it will use to know to what type it must deserialize. This manifest is defined as implicit in Genson, meaning that it will try to get it from implicitly available manifests in the "caller code". However in your original version there is no Manifest[T] in DeserializeFromJson.
An alternate way would be to define the DeserializeFromJson like that (which will just produce a constructor with an implicit Manifest[T] argument):
abstract class DeserializeFromJson[T: Manifest] {
def fromJson( json : String ) : T = JSON.parser.fromJson[T](json)
}
object Foo extends DeserializeFromJson[Foo]
More generally if you don't bring more value by encapsulating a lib (in this case Genson), I think you shouldn't do that. As you basically reduce the features of Genson (now people can only work with strings) and introduce problems like the one you hit.

I think your type parameter constraint were originally wrong;
you had
trait DeserializeFromJson[T <: DeserializeFromJson[T]]
With your own answer, you fully relaxed it; you needed
trait DeserializeFromJson[T <: SerializeToJson]
...which the error was trying to tell you.
The need for the implicit Manifest (ClassTag now I believe) or context-bounds was on the money.
Would be nice for Scala to allow the specification of inheritance and type-parameter constraints based on class/trait and companion object relationship, given it is already aware, to some degree, when it comes to access-modifiers and implicit scopes.

Related

Scala - Create user-type with method

I have a generic class which takes one type, and I have a parameter from that type. The issue is that I need to use a method from this parameter (in my problem, I need to use the method getTimestamp).
I can't have a trait or an abstract class because I know that, at some point, the type will be a Java Class that I can't modified.
I tried something like this :
type InputWithTimestamp = {def getTimestamp: Long}
class Foo[Input <: InputWithTimestamp](f : Input) {
def printTimestamp = { println(f.getTimestamp) }
}
class Test(timestamp : Long) {
def getTimestamp = timestamp
}
val t = new Test(1000)
val f = new Foo(t)
f.printTimestamp
And it is perfectly working.
But as I said, at some point I need to use a java class as a type.
And here is my problem :
Even if the java class defined a method getTimestamp which returns a long, I have the following error :
Error: inferred type arguments [MyJavaClass] do not conform to class Foo's type parameter bounds [Input <: InputWithTimestamp]
So what can I do to have a generic type which defined this method without needing to modify my java class ?
The problem in my case was that I didn't use parentheses to declare my method getTimestamp (because you get a warning in IDEA if you do so).
But in that case, I need the parentheses, otherwise the java method getTimestamp doesn't match the scala method
type InputWithTimestamp = {def getTimestamp: Long}
works perfectly.

Is using Any for Union type a good idea?

I want to create a class generator (for Avro models). I have a problem because sometime the field of the class I generate could be one of many different types, let's say it could be an Int or a String or whatever. The simple idea is to create that field with the type Any and check at runtime that it is ok.
The problem is that using Any discard some of the powerfull features of the scala type system. Some bug in the code will not be caught at compile time (if I give a List to a function expecting a String or Int covered by an Any).
For example :
Let's I have this description of my class :
{"className" : "MyClass", "fields" : [
{ "fieldName" : "myField", "type" : ["String", "Int"]}
]}
From this description, I create this class :
class MyClass(myField: Any)
I want to create something like this :
class MyClass(myField: String or Int)
Should I stop using Any ? Is using Any generally considered a good idea in the scala community ?
Is using Any generally considered a good idea in the scala community?
Nope. Any means no type information, so it's generally considered a bad practice.
In Scala you can express a union type using Either, although it gets cumbersome if you have a lot of possible types in the union. Example:
class MyClass(myField: Either[String, Int]) {
def doSomething = myField match {
case Left(myStringField) => ???
case Right(myIntField) => ???
}
}
Another viable approach would be to make MyClass generic in its type:
class MyClass[A](myField: A)
However this is not setting any constraint on the type of A.
In order to place a constraint, e.g. make it a finite subset of types, you can use ad-hoc polymorphism:
trait MyConstraint[A]
class MyClass[A: MyConstraint](myField: A)
Now new MyClass(myValue) won't compile unless there is an implicit MyConstraint[A] in scope. Now you can whitelist the types you want to allow using implicit values
implicit object IntConstraint extends MyConstraint[Int]
implicit object StringConstraint extends MyConstraint[String]
Example:
new MyClass(42) // ok, there's implicit evidence of MyConstraint[Int]
new MyClass("foo") // ok, there's implicit evidence of MyConstraint[String]
new MyClass(false) // won't compile, no implicit evidence of MyConstraint[Boolean]
In technical terms, MyConstraint is a type class, used to refine the type A in the constructor of MyClass.
You can characterize a type class even further, by requiring that a set of operations are defined for each of its instances. E.g.
trait MyConstraint[A] {
def mandatoryOp: A
}
implicit object IntConstraint extends MyConstraint[Int] {
def mandatoryOp = 42
}
implicit object StringConstraint extends MyConstraint[String] {
def mandatoryOp = "foo"
}
class MyClass[A](myField: A)(implicit ev: MyConstraint[A]) {
def doSomething: A = ev.mandatoryOp
}
Please note that A: MyConstraint is just syntactic sugar for requiring an implicit parameter of type MyConstraint[A]. In the last example I chose the explicit syntax in order to have the implicit parameter ev available in scope.

Context bounds for type members or how to defer implicit resolution until member instantiation

In the following example, is there a way to avoid that implicit resolution picks the defaultInstance and uses the intInstance instead? More background after the code:
// the following part is an external fixed API
trait TypeCls[A] {
def foo: String
}
object TypeCls {
def foo[A](implicit x: TypeCls[A]) = x.foo
implicit def defaultInstance[A]: TypeCls[A] = new TypeCls[A] {
def foo = "default"
}
implicit val intInstance: TypeCls[Int] = new TypeCls[Int] {
def foo = "integer"
}
}
trait FooM {
type A
def foo: String = implicitly[TypeCls[A]].foo
}
// end of external fixed API
class FooP[A:TypeCls] { // with type params, we can use context bound
def foo: String = implicitly[TypeCls[A]].foo
}
class MyFooP extends FooP[Int]
class MyFooM extends FooM { type A = Int }
object Main extends App {
println(s"With type parameter: ${(new MyFooP).foo}")
println(s"With type member: ${(new MyFooM).foo}")
}
Actual output:
With type parameter: integer
With type member: default
Desired output:
With type parameter: integer
With type member: integer
I am working with a third-party library that uses the above scheme to provide "default" instances for the type class TypeCls. I think the above code is a minimal example that demonstrates my problem.
Users are supposed to mix in the FooM trait and instantiate the abstract type member A. The problem is that due to the defaultInstance the call of (new MyFooM).foo does not resolve the specialized intInstance and instead commits to defaultInstance which is not what I want.
I added an alternative version using type parameters, called FooP (P = Parameter, M = Member) which avoids to resolve the defaultInstance by using a context bound on the type parameter.
Is there an equivalent way to do this with type members?
EDIT: I have an error in my simplification, actually the foo is not a def but a val, so it is not possible to add an implicit parameter. So no of the current answers are applicable.
trait FooM {
type A
val foo: String = implicitly[TypeCls[A]].foo
}
// end of external fixed API
class FooP[A:TypeCls] { // with type params, we can use context bound
val foo: String = implicitly[TypeCls[A]].foo
}
The simplest solution in this specific case is have foo itself require an implicit instance of TypeCls[A].
The only downside is that it will be passed on every call to foo as opposed to just when instantiating
FooM. So you'll have to make sure they are in scope on every call to foo. Though as long as the TypeCls instances are in the companion object, you won't have anything special to do.
trait FooM {
type A
def foo(implicit e: TypeCls[A]): String = e.foo
}
UPDATE: In my above answer I managed to miss the fact that FooM cannot be modified. In addition the latest edit to the question mentions that FooM.foo is actually a val and not a def.
Well the bad news is that the API you're using is simply broken. There is no way FooM.foo wille ever return anything useful (it will always resolve TypeCls[A] to TypeCls.defaultInstance regardless of the actual value of A). The only way out is to override foo in a derived class where the actual value of A is known, in order to be able to use the proper instance of TypeCls. Fortunately, this idea can be combined with your original workaround of using a class with a context bound (FooP in your case):
class FooMEx[T:TypeCls] extends FooM {
type A = T
override val foo: String = implicitly[TypeCls[A]].foo
}
Now instead of having your classes extend FooM directly, have them extend FooMEx:
class MyFoo extends FooMEx[Int]
The only difference between FooMEx and your original FooP class is that FooMEx does extend FooM, so MyFoo is a proper instance of FooM and can thus be used with the fixed API.
Can you copy the code from the third party library. Overriding the method does the trick.
class MyFooM extends FooM { type A = Int
override def foo: String = implicitly[TypeCls[A]].foo}
It is a hack, but I doubt there is anything better.
I do not know why this works the way it does. It must be some order in which the type alias are substituted in the implicitly expression.
Only an expert in the language specification can tell you the exact reason.

Passing a type parameter for instantiation

Why wouldn't the scala compiler dig this:
class Clazz
class Foo[C <: Clazz] {
val foo = new C
}
class type required but C found
[error] val a = new C
[error] ^
Related question - How to get rid of : class type required but T found
This is a classic generic problem that also happens in Java - you cannot create an instance of a generic type variable. What you can do in Scala to fix this, however, is to introduce a type evidence to your type parameter that captures the runtime type:
class Foo[C <: Clazz](implicit ct: ClassTag[C]) {
val foo = ct.runtimeClass.newInstance
}
Note that this only works if the class has a constructor without any arguments. Since the parameter is implicit, you don't need to pass it when calling the Foo constructor:
Foo[Clazz]()
I came up with this scheme, couldn't simplify it through a companion object thought.
class Clazz
class ClazzFactory {
def apply = new Clazz
}
class Foo(factory: ClazzFactory) {
val foo: Clazz = factory.apply
}
It's very annoying that ClazzFactory can't be an object rather than a class though. A simplified version:
class Clazz {
def apply() = new Clazz
}
class Foo(factory: Clazz) {
val foo: Clazz = factory.apply
}
This requires the caller to use the new keyword in order to provide the factory argument, which is already a minor enough annoyance relative to the initial problem. But, scala could have made this scenario all more elegant; I had to fallback here to passing a parameter of the type I wish to instantiate, plus the new keyword. Maybe there's a better way.
(motivation was to instantiate that type many times within the real Foo, that's why this is at all a solution; otherwise my pattern above is just redundantly meaningless).

Subclassing classes that have type parameters

Trying some basic graphing stuff in scala and ran into something which I think SO community can help with.
First. I have a graph trait defined
trait GraphLike[T] { ... }
and a sub-class
class DiGraph[T] extends GraphLike[T] { ... }
I also have a BreadthFirstSearch Class which looks like this:
class BreadthFirstSearch[ T ]( val graph: GraphLike[T], val sourceVertex: T ) extends Graphsearch.GraphSearch[ T ]{
I'm trying to use this in another class which looks like this:
class SAP[T]( val graph: DiGraph[T]) {
class inSap[T]( val v: T, val w: T )
{
val bfsV = new BreadthFirstSearch[T](graph, v)
}
}
I'm running into a compiler error when calling new BreadthFirstSearch.
BreadthFirstSearch takes a GraphLike parameter. The SAP class has a graph: DiGraph[T] and DiGraph is a subclass of GraphLike, so I would expect to be able to call the constructor of BreadthFirstSearch using a graph parameter of type DiGraph because DiGraph is GraphLike.
However, the compiler says this:
type mismatch; found : com.KGraph.DiGraph[T(in class SAP)] required: com.KGraph.GraphLike[T(in class
inSap)]
Its doesn't like me using the sub-class DiGraph in place of a GraphLike. Any idea why this would happen? I'm guessing this may have to do with the Type Parameters. All the classes are invariant on the type parameter.
I didn't think anything other than the class constructor definitions were needed to help with this, however, if more code is needed to figure stuff out, please update and I will provide the code.
When you defined your inner class:
class inSap[T]( val v: T, val w: T )
You introduced a second type variable named T. So when you call new BreadthFirstSearch[T] that is using the T from inSap, not the T from SAP. You should just need to remove the type parameter from inSap, so that inside that class, T just refers the the type in the outer scope.