Inheritance in case classes - scala

I have the following case class definition:
case class FileMetadata(announceUrls: List[String], pieceLength: Int, pieces: String) {
case class SingleFileMetadata(filename: String, length: Int)
case class MultiFileMetadata(dirname: String, files: List[FileInfo])
}
because I want both the single and multi file types to have all the same data values as the enclosing FileMetaData case class. This doesn't really seem to work though (in that I can't create a new SingleFileMetadata or MultiFileMetaData class.
Is there a way to do this in Scala?

In your current code, SingleFileMetadata and MultiFileMetadata do not inherit from FileMetadata. In fact, they only exist within specific instances of FileMetadata, which you most certainly do not want. Furthermore, you cannot extend a case class, as all of the members are private, so FileMetadata would have to be a class (or trait).
Something like this might work for you:
class FileMetadata(announceUrls: List[String], pieceLength: Int, pieces: String)
case class SingleFileMetadata(filename: String, length: Int, announceUrls: List[String], pieceLength: Int, pieces: String) extends FileMetadata(announceUrls, pieceLength, pieces)
case class MultiFileMetadata(dirname: String, files: List[FileInfo], announceUrls: List[String], pieceLength: Int, pieces: String) extends FileMetadata(announceUrls, pieceLength, pieces)

You can make your base class a trait and make both case classes inherit from it.
trait FileMetadata {
def announceUrls: List[String]
def pieceLength: Int
def pieces: String
}
case class SingleFileMetadata(filename: String, length: Int, announceUrls: List[String], pieceLength: Int, pieces: String) extends FileMetadata
case class MultiFileMetadata(dirname: String, files: List[FileInfo], announceUrls: List[String], pieceLength: Int, pieces: String) extends FileMetadata
One additional advantage of using trait is that you can limit the inheritance to only the classes defined in the given file, by specifying that trait as a sealed trait.

Alternative solution to your problem would be to create only one generic case class, set optional parameters to default value None, with flexible pattern matching you can easily perform operation on this class.
case class GenericFileMetadata(filename:Option[String] = None,
length:Option[String] = None,
dirname:Option[String] = None,
files:Option[List[FileInfo]]= None,
announceUrls: List[String], pieceLength: Int, pieces: String)

Related

Ducktyping implicit conversion in Scala

Is it possible to have a duck-typing style conversion where a converter will accept an arbitrary object and will try to convert it to the destination type?
case class RequiredFields(name: String, surname: String)
case class Person(name: String, surname: String, age: Option[Int])
implicit def arbitraryToPerson(object: ????): Person = Person(object.name, object.surname, None)
Arbitrary object doesn't have to be related to RequriedField class in any way, just the fields must exist.
I think you can combine structural typing and implicit conversions. Like so:
type RequiredFields = {
def name: String
def surname: String
}
case class NotAPerson(name: String, surname: String)
case class Person(name: String, surname: String, age: Option[Int])
implicit def arbitraryToPerson(o: RequiredFields): Person = Person(o.name, o.surname, None)
val p:Person = NotAPerson("oh", "my")
Are you sure that's a good idea, though? I think the much better pattern here would be to define a type class with the required fields. Have you considered that?
As hasumedic answered you probably want to use a trait.
If for some reason you don't want to use a trait (you don't have control over the package objects that are being sent to you etc) you can also use reflection to get the fields. (I don't actually know if this is a good idea, but my best guess is that it isn't)
case class RequiredFields(name: String, surname: String)
val rF = new RequriedFields("Hey", "yo")
val method = rF.getClass.getDeclaredMethod("name")
Then you get your value by invoking the getter for that field that scala makes for you
scala> val x = method.invoke(rF).asInstanceOf[String]
x: String = Hey
I think that you're looking for a trait:
trait Nameable {
def name: String
def surname: String
}
case class Person(name: String, surname: String, age: Option[Int]) extends Nameable
implicit def arbitraryToPerson(variable: Nameable): Person = Person(variable.name, variable.surname, None)
You can think of a trait like an interface. A characteristic of Scala traits is that they also accept implementations of its methods.
Classes extending this trait will need to implement the methods name and surname. In this case, the only valid Nameable parameter would be a Person. Having more classes that extend the trait would allow for some sort of polymorphism, making this implicit actually useful.
EDIT
From your comment and negative, just to point out that you can call your traits however you like. In your case, something like Nameable could work. It doesn't mean that there's a hierarchy, but that your object fulfils certain conditions, such as implementing those methods. Classes can extend more than one trait in Scala, using the with keyword.
case class Dog(name: String, surname: String) extends Animal with Nameable
In this case, your dog would be an animal, but you can enforce the implementation of the Nameable methods by extending the trait.
It seems you can do ducktyping driven conversion in the following way:
implicit def convertToPerson(obj: {def name(value: String): String; def surname(): String}): Person = {
Person(obj.name, obj.surname, None)
}

Implicitly convert parameter Option[T] to T in a case class

I had a case class with a option parameter, let's say:
case class Student(id: Option[Int], name: String)
To get a Student instance, not only I could use Student(Some(1), "anderson"), I also want this form to be a valid way Student(2,"Sarah")
I guess I have to create a Int => Option[Int] and put it somewhere. So what's the best way to do so?
Update
As mentioned in the comment, override apply method will block calling it by Student.apply _
It might be easier to just make an apply method in a companion object.
case class Student(id: Option[Int], name: String)
object Student {
def apply(id: Int, name: String): Student = {
Student(Some(id), name)
}
}
An alternative solution using implicit conversions:
implicit def intToOption(x: Int) = Some(x)
case class Student(id: Option[Int], name: String)
scala> Student(1,"Nu")
res1: Student = Student(Some(1),Nu)

Transform one case class into another when the argument list is the same

I have a lot of similar case classes which mean different things but have the same argument list.
object User {
case class Create(userName:String, firstName: String, lastName: String)
case class Created(userName:String, firstName: String, lastName: String)
}
object Group {
case class Create(groupName:String, members: Int)
case class Created(groupName:String, members: Int)
}
Given this kind of a setup, I was tired of writing methods that take an argument of type Create and return an argument of type Created. I have tons of test cases that do exactly this kind of thing.
I could write a function to convert one case class into the other. This function converts User.Create into User.Created
def userCreated(create: User.Create) = User.Create.unapply(create).map((User.Created.apply _).tupled).getOrElse(sys.error(s"User creation failed: $create"))
I had to write another such function for Group.
What I'd really like to have is a generic function that takes the two types of the case classes and an object of one case class and converts into the other. Something like,
def transform[A,B](a: A):B
Also, this function shouldn't defeat the purpose of reducing boilerplate. Please feel free to suggest a different signature for the function if that's easier to use.
Shapeless to the rescue!
You can use Shapeless's Generic to create generic representations of case classes, that can then be used to accomplish what you're trying to do. Using LabelledGeneric we can enforce both types and parameter names.
import shapeless._
case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
case class SortOfCreated(screenName: String, firstName: String, lastName: String)
val c = Create("username", "firstname", "lastname")
val createGen = LabelledGeneric[Create]
val createdGen = LabelledGeneric[Created]
val sortOfCreatedGen = LabelledGeneric[SortOfCreated]
val created: Created = createdGen.from(createGen.to(c))
sortOfCreatedGen.from(createGen.to(c)) // fails to compile
For the record, here is the simplest typesafe syntax I've managed to implement:
implicit class Convert[A, RA](value: A)(implicit ga: Generic.Aux[A, RA]) {
def convertTo[B, RB](gb: Generic.Aux[B, RB])(implicit ev: RA =:= RB) =
gb.from(ga.to(value))
}
And it can be used like this:
case class Create(userName: String, firstName: String, lastName: String)
case class Created(userName: String, firstName: String, lastName: String)
val created = Create("foo", "bar", "baz").convertTo(Generic[Created])
Or the same thing with LabelledGeneric to achieve better type safety:
implicit class Convert[A, RA](value: A)(implicit ga: LabelledGeneric.Aux[A, RA]) {
def convertTo[B, RB](gb: LabelledGeneric.Aux[B, RB])(implicit ev: RA =:= RB) =
gb.from(ga.to(value))
}
val created = Create("foo", "bar", "baz").convertTo(LabelledGeneric[Created]))

Scala case class structure for complex xml

xml data looks like
<bd>
<oied>
<oeuo>XYZWC999</oeuo>
<oedo>SorNbteluk=ONRM_ROOT_MO_R,SrobeNhbwk=XYZWC999,MoetxeoCt=XYZWC999</oedo>
<oesw>CXP9021776/2_R2CA15</oesw>
</oied>
<bi>
<bts>20150205141500Z</bts>
<gp>900</gp>
<bt>paaoCukStSteboshRrttcps</bt>
<bt>pptubthCaStctoSekSos</bt>
<bv>
<biod>MaebdlgeaooeEt=1,TparswotterNorok=1,Ntcp=Kub-9</biod>
<r>4578</r>
<r>10769</r>
</bv>
<bv>
<biod>MEegoedbaaloet=1,TreatoorNtosrpwk=1,Ntcp=1</biod>
<r>11021</r>
<r>86235</r>
</bv>
<bv>
<biod>MdaolaeeobeEgt=1,TretrowooNrtsapk=1,Nctp=Kub-7</biod>
<r>0</r>
<r>0</r>
</bv>
</bi>
</bd>
I am new to Scala, I could figure out the basic structure.
case class xmldata(oeuo : String, oedo : String, oesw: String, bts: String, gp : Int, btArray : List[String])
What is the optimized Scala case class (with collections) for this xml data?
It's just tree of case classes:
case class Bd(oied: Oied, bi: Bi)
case class Oied(oeuo: String, oedo: String, oesw: String)
case class Bi(bts: String, gp: String, bt: List[String], bv: List[Bv])
case class Bv(biod: String, r: List[String])
If order doesn't matter - you might use Set instead of List
You also might flatten it a bit (like in your solution), but it could be harder to map it with xml-binding tools then
case class Bd(oeuo: String, oedo: String, oesw: String, bts: String, gp: String, bt: List[String], bv: List[Bv])
The most flattened and least operable version (not recommended):
case class Bd(oeuo: String, oedo: String, oesw: String, bts: String, gp: String, bt: List[String], biods: List[String], rs: List[List[String]])

Extending a scala class with less ceremony

I am probably doing something wrong.
When I extend a class, I specify the mapping between the extendee's constructor and the extended class constructor:
class Base(one: String, two: String)
case class Extended(one: String, two: String, three: String) extends Base(one, two)
How can I instead suffice with something like any of the following, thus implying a "default" mapping?
class Base(one: String, two: String)
case class Extended(one: String, two: String, three: String) extends Base
class Base(one: String, two: String)
case class Extended(three: String) extends Base
I am probably missing the cleaner way of just adding a parameter without all that ceremony.
Or should I be using a trait rather than subclassing, for such simple thing....
All the parameters of a case class generated apply method have to be specified in the case class declaration, so your second proposal cannot work. The first one can be accomplished if Base is abstract, using abstract vals:
abstract class Base {
// no initializers!
val one : String
val two : String
def print { println("Base(%s, %s)" format (one, two)) }
}
// Note: constructor arguments of case classes are vals!
case class Extended(one: String, two: String, three: String) extends Base
...
Extended("foo", "bar", "baz").print // prints 'Base(foo, bar)'
However, the names need to match exactly.