Prohibit generating of apply for case class - scala

I'm writing a type-safe code and want to replace apply() generated for case classes with my own implementation. Here it is:
import shapeless._
sealed trait Data
case object Remote extends Data
case object Local extends Data
case class SomeClass(){
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
}
val t: SomeClass = SomeClass() // <------------------ still compiles, bad
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles, good
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //does not compile, good
I want to prohibit val t: SomeClass = SomeClass() from compiling. Is it possible to do somehow except do not SomeClass to be case class?

There is a solution that is usually used if you want to provide some smart constructor and the default one would break your invariants. To make sure that only you can create the instance you should:
prevent using apply
prevent using new
prevent using .copy
prevent extending class where a child could call the constructor
This is achieved by this interesing patten:
sealed abstract case class MyCaseClass private (value: String)
object MyCaseClass {
def apply(value: String) = {
// checking invariants and stuff
new MyCaseClass(value) {}
}
}
Here:
abstract prevents generation of .copy and apply
sealed prevents extending this class (final wouldn't allow abstract)
private constructor prevents using new
While it doesn't look pretty it's pretty much bullet proof.
As #LuisMiguelMejíaSuárez pointed out this is not necessary in your exact case, but in general that could be used to deal with edge cases of case class with a smart constructor.
UPDATE:
In Scala 3 you only need to do
case class MyCaseClass private (value: String)
and it will prevent usage of: apply, new and copy from outside of this class and its companion.
This behavior was ported to Scala 2.13 with option -Xsource:3 enabled. You have to use at least 2.13.2 as in 2.13.1 this flag doesn't fix the issue.

So you can make the constructor private and ensure that T is also something different to Nothing.
I believe the best way to ensure the constructor is private (as well as many other things as #MateuszKubuszok show) is to use a (sealed) trait instead of a class:
(if you can not use a trait for whatever reasons, please refer to Mateusz's answer)
import shapeless._
sealed trait Data
final case object Remote extends Data
final case object Local extends Data
sealed trait SomeClass {
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev1: TT =:!= Data, ev2: TT =:!= Nothing): Aux[TT] =
new SomeClass { override final type T = TT }
}
Which works like this:
SomeClass() // Does not compile.
SomeClass.apply[Remote.type] // Compiles.
SomeClass.apply[Data] // Does not compile.
You can see it running here.

If you want to prohibit using some of auto-generated methods of a case class you can define the methods (with proper signature) manually (then they will not be generated) and make them private (or private[this]).
Try
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
private def apply(): SomeClass = ??? // added
}
val t: SomeClass = SomeClass() // doesn't compile
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //doesn't compile
In principle, the methods (apply, unapply, copy, hashCode, toString) can be generated not by compiler itself but with macro annotations. Then you can choose any subset of them and modify their generation as you want.
Generate apply methods creating a class
how to efficiently/cleanly override a copy method
Also the methods can be generated using Shapeless case classes a la carte. Then you can switch on/off the methods as desired too.
https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/alacarte.scala
https://github.com/milessabin/shapeless/blob/master/core/src/test/scala/shapeless/alacarte.scala

Related

Managing trait instance consumers in a generic way

Let's imagine I have the following base trait and case classes
sealed trait BaseTrait
case class Foo(x: Integer = 1) extends BaseTrait
case class Bar(x: String = "abc") extends BaseTrait
I would like to create a generic interface for classes which can process BaseTrait instances, something like the following
class FooProcessor(val model: FooModel) extends BaseProcessor[Foo] {
val v: Option[Foo] = model.baseVar
}
class BarProcessor(val model: BarModel) extends BaseProcessor[Bar] {
val v: Option[Bar] = model.baseVar
}
For this I have the following traits
trait BaseModel[T <: BaseTrait] {
var baseVar: Option[T] = None
}
trait BaseProcessor[T <: BaseTrait] {
def model: BaseModel[T]
def process(x: T): Unit = model.baseVar = Option(x)
}
The model definitions are the following
class FooModel extends BaseModel[Foo]
class BarModel extends BaseModel[Bar]
Now lets imagine I have the following processors somewhere in my app
val fooProcessor = new FooProcessor(new FooModel)
val barProcessor = new BarProcessor(new BarModel)
I would like to handle them in a somewhat generic way, like this
def func[T <: BaseTrait](p: T) {
val c/*: BaseProcessor[_ >: Foo with Bar <: BaseTrait with Product with Serializable]*/ = p match {
case _: Foo => fooProcessor
case _: Bar => barProcessor
c.process(p)
}
The compiler is not really happy about the last line, it says
type mismatch;
found : T
required: _1
If I understand correctly this is basically the compiler trying to prevent barProcessor.process(Foo()) from happening. I've tried a couple of solutions to get around this and achieve the desired behavior:
the simplest way around this is calling the proper *Processor.process with the proper BaseTrait instance inside the match case, which seems to defy the whole point of handling them in a somewhat generic way
use an abstract type in the BaseModel and BaseProcessor, which one hand got rid of the somewhat unneeded type parameter in BaseModel but the compilers complaint is still valid and I was not able to figure out if it's possible to get that to work
get rid of the type parameter and contraint from the BaseModel, and just do a type cast in the processor to get the proper type, but the explicit type cast also isn't really what I was hoping for
Like so:
trait BaseModel {
var baseVar: Option[BaseTrait] = None
}
trait BaseProcessor[T <: BaseTrait] {
def model: BaseModel
def process(x: T): Unit = model.baseVar = Some(x)
def getBaseValue: T = model.baseVar.map(_.asInstanceOf[T])
}
I guess one could also somehow convince the compiler that the two types (T of the Processor and T of the func parameter p) are equivalent, but that also seems like an overkill (and I'm also not really sure how it can be done).
So my question is the following: is it possible to do what I'm trying to achieve here (managing processors in a uniform way + each processor knows their specific type of BaseTrait) in a somewhat easy fashion? Is there a better model for this which I'm missing?
Update
As per Tim's answer making the controllers implicit solves the problem, however if you want to have a class where you define your controllers + have 'func' on it's interface the compiler no longer seems to properly resolve the implicits. So if I try to do something like this
class ProcessorContainer {
implicit val fooProcessor = new FooProcessor(new FooModel)
implicit val barProcessor = new BarProcessor(new BarModel)
def func[T <: BaseTrait](p: T) = typedFunc(p)
private def typedFunc[T <: BaseTrait](p: T)(implicit processor: BaseProcessor[T]) =
processor.process(p)
}
class Test {
val processorContainer = new ProcessorContainer
processorContainer.func(Foo())
processorContainer.func(Bar())
}
I get the following compile error (one for Foo and one for Bar):
could not find implicit value for parameter processor: BaseProcessor[Foo]
not enough arguments for method
Is there a way around this? I could of course expose the controllers so they can be passed in implicitly, however I'd prefer not doing that.
You can create a simple typeclass by making the processors implicit and passing them as an extra argument to func:
implicit val fooProcessor = new FooProcessor(new FooModel)
implicit val barProcessor = new BarProcessor(new BarModel)
def func[T <: BaseTrait](p: T)(implicit processor: BaseProcessor[T]) =
processor.process(p)
If you pass a Foo to func it will call FooProcessor.process on it, and if you pass a Bar to func it will call BarProcessor on it.

In Scala, How to reference the ClassTag of implemented abstract type in all subclasses?

If I have 1 trait and 2 objects:
trait MyClass {
type T <: MyClass
def foo(): ClassTag[T] = {...}
}
object ChildClass1 extends MyClass {
type T = String
}
object ChildClass2 extends MyClass {
type T = Option[String]
}
is it possible to implement foo() in MyClass, such that ChildClass1.foo() yields ClassTag[String], and ChildClass2.foo() yields ClassTag[Option].
If not, what's the easiest way to bypass it? It should be noted that the implementation of T may be inner classes/objects, so hacking reflection may have some side effects.
Ok, it's possible that I don't completely understand your goal, but from what I can see, you are trying to create a trait with an upper bound type, and you also want to be able to get the type at runtime, correct?
So let's assume you have a Foo trait:
class MyClass // not important
trait Foo[T <: MyClass] {
def foo: ClassTag[T]
}
If you want an object implementation, the solution is trivial, since you know the type at compile time:
class MyClassSubclass extends MyClass // also not important
object FooObject extends Foo[MyClassSubclass] {
def foo: ClassTag[MyClassSubclass] = ClassTag(classOf[MyClassSubclass])
}
But if you want a class, then you can solve the problem with the implicitly + context bound combo in a pretty readable way:
class FooImpl[T <: MyClass : ClassTag] extends Foo[T] {
def foo: ClassTag[T] = implicitly[ClassTag[T]]
}
I would like to propose a dirty and impaired answer, please advice me if you have any better idea:
lazy val mf: ClassTag[T] = {
val clazz = this.getClass
val name = clazz.getName
val modifiedName = name + "T"
val reprClazz = Utils.classForName(modifiedName)
Manifest.classType(reprClazz)
}
it only works if the subclass is a singleton object.

Scala reflection to instantiate scala.slick.lifted.TableQuery

I have this base trait
trait MyBase {
type M
type T <: Table[M]
val query: TableQuery[T]
}
Where TableQuery is scala.slick.lifted.TableQuery
My subclasses instantiate TableQuery like so:
type M = Account
type T = AccountsTable
val query = TableQuery[T]
I'd like to instantiate the TableQuery in the base trait, possibly by using a lazy val, i.e.
lazy val query: TableQuery[T] = {
...
}
I've been playing around with reflection, but haven't had much luck.
If I understand correctly, what you want is to be able to extend
MyBase by simply defining M and T but without having to explicitly instantiate the TableQuery in each derived class.
Using reflection is not really an option because normally you use TableQuery.apply
for that (as in val query = TableQuery[MyTable]), and this is implemented through a macro,
so you've got a "runtime vs compile-time" issue.
If you absolutely need MyBase to be a trait (as opposed to a class), then I don't see any viable solution.
However if you can turn MyBase into a class and turn M and T into type parameters (instead of abstract types), then there is at least one solution.
As I hinted in another related question (How to define generic type in Scala?), you can
define a type class (say TableQueryBuilder) to capture the call to TableQuery.apply (at the point where the concrete type is known) along with an implicit macro (say TableQueryBuilder.builderForTable) to provide
an instance of this type class. You can then define a method (say TableQueryBuilder.build) to actually instantiate the TableQuery, which will just delegate to job to the type class.
// NOTE: tested with scala 2.11.0 & slick 3.0.0
import scala.reflect.macros.Context
import scala.language.experimental.macros
object TableQueryBuilderMacro {
def createBuilderImpl[T<:AbstractTable[_]:c.WeakTypeTag](c: Context) = {
import c.universe._
val T = weakTypeOf[T]
q"""new TableQueryBuilder[$T]{
def apply(): TableQuery[$T] = {
TableQuery[$T]
}
}"""
}
}
trait TableQueryBuilder[T<:AbstractTable[_]] {
def apply(): TableQuery[T]
}
object TableQueryBuilder {
implicit def builderForTable[T<:AbstractTable[_]]: TableQueryBuilder[T] = macro TableQueryBuilderMacro.createBuilderImpl[T]
def build[T<:AbstractTable[_]:TableQueryBuilder](): TableQuery[T] = implicitly[TableQueryBuilder[T]].apply()
}
The net effect is that you don't need anymore to know the concrete value of the type T in order to be able to instantiate a TableQuery[T],
provided that you have an implicit instance of TableQueryBuilder[T] in scope. In other words, you can shift the need to know the concrete value of T
up to the point where you actually know it.
MyBase (now a class) can then be implemented like this:
class MyBase[M, T <: Table[M] : TableQueryBuilder] {
lazy val query: TableQuery[T] = TableQueryBuilder.build[T]
}
And you can then extend it without the need to explcitly call TableQuery.apply:
class Coffees(tag: Tag) extends Table[(String, Double)](tag, "COFFEES") {
def name = column[String]("COF_NAME")
def price = column[Double]("PRICE")
def * = (name, price)
}
class Derived extends MyBase[(String, Double), Coffees] // That's it!
What happens here is that in Derived's constructor, an implicit value for TableQueryBuilder[Coffees] is implicitly
passed to MyBase's constructor.
The reason why you cannot apply this pattern if MyBase were a trait is pretty mundane: trait constructors cannot have parameters, let alone implicit parameters, so there would be no implicit way
to pass the TableQueryBuilder instance.

Return class instance using generic inside companion object for method specified in trait

In Scala I want to return a instance of a class for a method defined in a trait which uses generics, the code example I have is this:
File 1
package packOne
import packTwo.A
trait MyTrait[T <: MyTrait[T <: A]] {
def otherFunct(): String
def funct[T <: A](): T
}
File 2
package packTwo
import packOne.MyTrait
abstract class A(someParameter: String) {}
class B(someParameter: String) extends A(someParameter) {}
object B extends MyTrait[B] { // <--- the B inside MyTrait here is the class not the object, or at least that is what I want
def otherFunct(): String = "Hello"
def funct[B](): C = new B("hi") // <--- I think here is the key
}
basically what I want is an interface that have method to return a concrete implementation of class A, in an implementing object (which happen to be a companion object for a class extending A).
Why do I want that to be on an object?, is because I want to call that method without the need of an instance (like an static method in java), so that I can call B.funct() and have an instance of B class kind of like a factory method, for other classes extending A for example a call to X.funct will return an instance of class X.
I have tried to remove the generic type from the function definition except on the return type of the function and just leave it in the trait definition (like def funct(): T) but that does not work either.
I am quite new to Scala so if you could explain it for dummies and avoid complex scala unique concepts I would appreciate
How about simply:
trait A
class B(someParameter: String) extends A
trait MyTrait[T <: A] {
def otherFunct: String //Parentheses on parameterless methods with no side effects and no serious computation are generally unidiomatic in Scala
def funct: T //Note, no generic parameter on this method
}
object B extends MyTrait[B] {
def otherFunct = "Hello"
def funct = new B("hi")
}
And then:
B.funct //returns a new `B`
The apply method is often used in this factory style (e.g. Seq.apply() which is equivalent to Seq())

Scala implicit conversion on generic trait implementing Java interface

I have been working on an issue with implicit conversion for days now, but somehow I just cannot figure out what I am doing wrong. I read through all the other questions on SO that deal with implicits but I still don't understand what the problem is.
As an example, let's consider a Java interface like this(T extends Object for brevity):
public interface JPersistable<T extends Object> {
public T persist(T entity);
}
In scala, I do the following:
case class A()
case class B() extends A
case class C()
case class D() extends C
trait Persistable[DTOType <: A, EntityType <: C] {
// this would be implemented somewhere else
private def doPersist(source: EntityType): EntityType = source
// this does not implement the method from the Java interface
private def realPersist(source: DTOType)(implicit view: DTOType => EntityType): EntityType = doPersist(source)
// this DOES implement the method from the Java interface, however it throws:
// error: No implicit view available from DTOType => EntityType.
def persist(source: DTOType): EntityType = realPersist(source)
}
case class Persister() extends Persistable[B, D] with JPersistable[B]
object Mappings {
implicit def BToD(source: B): D = D()
}
object Test {
def main(args: Array[String]) {
import Mappings._
val persisted = Persister().persist(B())
}
}
As stated in the comment, I get an exception at compile time. I guess my questions are:
1) Why do I need to specify the implicit conversion on the doRealPersist explicitly? I expected the conversion to happen even if I do the following:
trait Persistable[DTOType <: A, EntityType <: C] {
// this would be implemented somewhere else
private def doPersist(source: EntityType): EntityType = source
def persist(source: DTOType): EntityType = doPersist(source)
}
However, this does not compile either.
2) Why does compilation fail at persist and not at the actual method call (val persisted = Persister().persist(B()))? That should be the first place where the actual type of EntityType and DTOType are known, right?
3) Is there a better way to do what I am trying to achieve? Again, this is not the actual thing I am trying to do, but close enough.
Apologies in advance if this question is ignorant and thanks a lot in advance for your help.
You need to make the conversion available within the trait. You can't pass it in from the outside implicitly because the outside doesn't know that persist secretly requires realPersist which requires an implicit conversion. This all fails even without considering JPersistable.
You can for example add
implicit def view: DTOType => EntityType
as a method in the trait and it will then compile. (You can drop realPersist then also.)
Then you need a way to get that view set. You can
case class Persister()(implicit val view: B => D) extends Persistable[B,D]
and then you're all good. (The implicit val satisfies the implicit def of the trait.)
But now you have bigger problems: your Java interface signature doesn't match your Scala signature. The equivalent Scala is
trait JPersistable[T <: Object] { def persist(t: T): T }
See how persist takes and returns the same type? And see how it does not in your Scala class? That's not going to work, nor should it! So you have to rethink exactly what you're trying to accomplish here. Maybe you just want to make the implicit conversion available--not pass it to the method!--and have Scala apply the implicit conversion for you so that you think you've got a persist that maps from DTOType to EntityType, but you really just have the EntityType to EntityType transform that the Java interface requires.
Edit: for example, here's a working version of what you posted just using standard implicit conversion:
trait JPer[T] { def persist(t: T): T }
class A
case class B() extends A
class C
case class D() extends C
trait Per[Y <: C] extends JPer[Y] {
private def doIt(y: Y): Y = y
def persist(y: Y) = doIt(y)
}
case class Perer() extends Per[D] // "with JPer" wouldn't add anything!
object Maps { implicit def BtoD(b: B): D = D() }
object Test extends App {
import Maps._
val persisted = Perer().persist(B())
}
Pay attention to which types are used where! (Who takes B and who takes D and which direction do you need a conversion?)