private keyword has different meaning on class definition - scala

I think I had a good understanding of Scala till I find myself in this simple scenario
sealed abstract case class Name private(name: String)
object Name {
def make(name: String): Option[Name] =
if (name.nonEmpty) Some(new Name(name) {}) else None
}
my question is about the private modifier for the class.
If I use it like this, everything works, but if I move the private keyword at the start, something like
private sealed abstract case class Name(name: String) it doesn't compile becuase gives me the following error
private class Name escapes its defining scope as part of type
Option[example.package.Name]
where example.package is the package object I'm working in.
I think I need some clarification because I'm not sure what's happening here

In
sealed abstract case class Name(name: String)
Name effectively denotes both
the name of the class
the default constructor for that class
sealed abstract case class Name private(name: String)
declares that the class is public, but the constructor is private (i.e. can only be called from within that class or its companion object).
private sealed abstract case class Name(name: String)
declares that the class is private (which implies that the constructor is also private).

This code compiles with no error:
private sealed abstract case class Name(name: String)
The problem is that you are then returning a value that contains a public value of this class, so the definition is leaking out and it isn't really private. You need to make sure that all references to Name are also private.
The first syntax marks the constructor as private;
sealed abstract case class Name private(name: String)
This means that the constructor can only be called from inside the Name companion object. So this is not allowed
val name = new Name("name") // constructor Name in class Name cannot be accessed

Related

Scala - what is case class private

I am analyzing my existing project, I found some this like this(conceptually):
case class AA private(id: String) {}
case class BB(id: String) {}
After I created those two classes to observe the difference. I analysed their java source by using java decompiler. I did not find any different.
What is the need of private there.
What is the importance of that.
A case class is a class which gets a Companion object automatically defined with a few helper functions. One of these is an apply method which essentially allows to skip out the 'new' keyword when defining a class. The private keyword in your example makes the constuction of a new AA using the 'new' keyword private. Eg:
case class A private(id: Int)
case class B(id: Int)
A(1) //Using public method
B(1) //Using public method
new A(1) // Using PRIVATE method
new B(1) // Using public method
You can understand this better using Scala REPL
scala> case class A private(a: String)
defined class A
scala> new A("")
<console>:14: error: constructor A in class A cannot be accessed in object $iw
new A("")
^
scala> A("")
res3: A = A()
Notice that instantiation of the A cannot be done using new keyword. private helps restrict the instantiation of A using new(makes it private)

Scala: force type parameter to be a case class

I have an abstract class Model from which I create case classes:
abstract class Model
case class User(.) extends Model
an abstract class Table taking such a Model as type parameter, used in one of its default concrete methods:
abstract class Table[M <: Model] {
def parser = SomeExternalBuilder[M]
}
The meaning is rather simple: "Give every instance of Table a default parser based on its own class".
The problem is that SomeExternalBuilder will only accept a case class as argument ("case class expected: M"), so it does not compile.
Can I make Table take only case classes as type parameter?
I have seen a few answers providing a missing copy method (ref1, ref2), so I tried this:
trait Model[T] {
def copy: T
}
abstract class Table[M <: Model[M]]
but now case class User extends Model[User] and must overwrite copy too, every function creating a Model takes a type parameter, and honestly the code quickly starts being atrocious, all that for that single line in Table.
Is there no better way than copying that def parser line in every child's body?
Edit: N.B. The real function is def parser: anorm.Macro.namedParser[M] from the "anorm" library for Play.
Edit: Source of the type check by this macro: https://github.com/playframework/anorm/blob/0a1b19055ba3e3749044ad8a54a6b2326235f7c8/core/src/main/scala/anorm/Macro.scala#L117
The problem is that SomeExternalBuilder will only accept a case class as argument ("case class expected: M"), so it does not compile.
I don't think you can ever get such a message from Scala compiler itself, which means that SomeExternalBuilder.apply is a macro. It requires a specific case class in order to know its fields, so that it doesn't matter if you could limit M to be a case class (which you can't): it still wouldn't accept a type parameter.
What you can do is create a macro annotation, so that when you write e.g.
#HasModel
class SomeTable extends Table[SomeModel] {
...
}
the val parser = namedParser[SomeModel] is generated automatically.
Alternately, write #HasModel[SomeModel] class SomeTable { ... } and generate extends Table[SomeModel] as well.
It wouldn't be hard (as macros go), but you still need to annotate each class extending Table.
Not fool proof solution but worth a try
case classes extend Product and Serialisable. Constraint Product with Serialisable will help you get some type safety. M can be any class which extends Product with Serialisable. But Product is extended by case class mostly
abstract class Table[M <: (Product with Serializable)] {
def parser = SomeExternalBuilder[M]
}

scala value class multiple inheritance

I have in my project objects that represent IDs.
Let's say it is ChairId, TableId, LampId. I want them all to inherit from GenericId. And I want to be able to call def f(x: GenericId) = x.id
I want them to hold only single id: String so I would like to make them extend AnyVal.
Also I would like for each type to provide function generate which would generate my specific ID i.e. I would like to type something like ChairId.generate()
I have typed this:
sealed abstract class GenericId(val id: String)
final case class ChairId(override val id: String) extends GenericId(id)
final case class TableId(override val id: String) extends GenericId(id
And I though if GenericId would inherit from AnyVal that would work but so far no luck ;/ I also tried making GenericId a trait and make case classes extend AnyVal with GenericId but also won't compile :/
Another thing with TableId.generate() I can provide companion object just with function generate and that basically solve my problem but I wondered if there is possibility to solve that without defining companion object? (i.e. through implicits somehow)
// edit
regarding comment to provide code which doesn't compile(and I would like to):
sealed abstract class AbstractId(val id: String) extends AnyVal
final case class CatId(override val id: String) extends AbstractId(id)
final case class DogId(override val id: String) extends AbstractId(id)
Value classes cannot work this way for a couple of reasons.
First, from the documentation, value classes cannot be extended by any other class, so AbstractId cannot extend AnyVal. (Limitation #7)
scala> abstract class AbstractId(val id: String) extends AnyVal
<console>:10: error: `abstract' modifier cannot be used with value classes
abstract class AbstractId(val id: String) extends AnyVal
^
Second, even if you make AbstractId a trait, and define the other ids like this:
final case class DogId(val id: String) extends AnyVal with AbstractId
.. the usage of the value class wouldn't fit your case, because the class itself would still get allocated. See the allocation summary:
A value class is actually instantiated when:
a value class is treated as another type.
a value class is assigned to an array.
doing runtime type tests, such as pattern matching.
Some quotes from the value classes SIP that are likely to clarify your doubts:
Value classes...
...must have only a primary constructor with exactly one public, val
parameter whose type is not a value class.
... cannot be extended by another class.
As per 1. it can not be abstract; per 2. your encoding doesn't work.
There is another caveat:
A value class can only extend universal traits and cannot be extended
itself. A universal trait is a trait that extends Any, only has defs
as members, and does no initialization. Universal traits allow basic
inheritance of methods for value classes, but they incur the overhead
of allocation.
With all that in mind, based on your last snippet, this might work:
sealed trait AbstractId extends Any { def id: String }
final case class CatId(id: String) extends AnyVal with AbstractId
final case class DogId(id: String) extends AnyVal with AbstractId
But keep in mind the allocation only occurs if you want to use CatId and DogId as an AbstractId. For better understanding I recommend reading the SIP.

Accessing parameters of the same name of different classes in Scala

I have a very specific scenario, in which I have some different abstract classes the have child case classes that can have different parameters, for example:
abstract class ball() {}
case class football(_name: String, _shape: String) extends ball
case class basketball(_name: String, _size: Int) extends ball
and a different abstract class:
abstract class food() {}
case class vegetarian(_name: String, calories: Int) extends food
case class meat(_name: String, proteinCount: Int) extends food
Now, the problem I'm facing is that I need to somehow extract the name of all of those without knowing what class it is, I just know that ALWAYS, EACH CLASS has a parameters named _name.
Supposing we have an object of any of above classes, I'm trying to do it like this:
object.getClass.getDeclaredField("_name").get(this)
But I'm getting the error:
can not access a member of class package.food with modifiers "private"
I tried putting val and var before parameters in class but it doesnt help. I also tried doing "setAccessible(true)" in a line before get(this), which also doesn't help.
The obvious clean solution would be to have a least a common trait to all these classes:
trait HasName {
def _name: String
}
and then you can safely do obj.asInstanceOf[HasName]._name. Better yet if you manage to keep around the static information that obj is a HasName, in which case obj._name suffices.
If you can't do any of that, reflection is the way to go. You can do it pretty easily using a structural type, in this case:
obj.asInstanceOf[{ def _name: String }]._name
Note that this will be slower than the above HasName solution, and completely unchecked at compile time.

How case class inherent class(not abstract) method and value? (scala)

I have a Base class have some function and val, I want to inherent them in my inherent case class how to do it ?
This is my base class:
class Base(val name:String, val number:int) extends Sometrait {
def copy(name:String=this.name, number:int=this.number){
new Base(name, number)
}
}
I want to write the:
case class SomeCase(val name:String, val number:int, val id:int)extends Base(String, number){
...
}
But the compiler always told me:
value **** needs `override' modifier social.scala /scalatest/src/scalatest line 35 Scala
But I really want to is just do as inherent not override, how to do it.
(I need to put the child class as case class, as it is easy for me to use in slick. (here is my another question for how to use class as table content class in slick, someone give me really great answer, but I still mass.))
Because name and number fields in both Base and SomeCase are defined with val without any modifiers like private, they are both public members and participate in inheritance. Because these fields have same names in base and child classes, you have to add override modifier before val name and val number in the child class:
case class SomeCase(override val name: String,
override val number: Int,
val id: Int) extends Base(name, number) { ... }