Ducktyping implicit conversion in Scala - 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)
}

Related

How to use an Object in a trait by using its type in Scala

I was trying to implement some Model - Row implementation similar that Rails (Activerecord) in Scala. I simply trying to do this;
trait Model[A]:
def query = "model has this good stuff I want to use in RowData"
trait RowData[B <: Model[B]]:
def query = B.query
case class Person(name: String, age: Int) extends RowData[Person]
object Person extends Model[Person]
Person("onur", 38).query // I wan't to use like this
I'm getting Not found: B on RowData trait. Because Model[A] is an object I supposed that I can use it as is.
You can try in here: https://scastie.scala-lang.org/U3MOJhFXSS2O8fanY5aIpg
trait RowData[B <: Model[B]]:
def query = B.query
This is not supported because (among other reasons) the query method might not exist.
The problem is that the type of the companion object is not related to the type of the class. So the fact that B is a subclass of Model[B] says nothing about the type of the companion object of B. And specifically, it does not require the companion object of B to be a subclass of Model[B] and therefore it is not required to have the appropriate query method.
The other problem is that this does not compile:
trait RowData[B <: Model[B]]:
def query = B.query
case class Person(name: String, age: Int) extends RowData[Person]
The RowData trait requires that Person is a subclass of Model[Person] but Person does not inherit from Model[Person]
You either need to have Person inherit from Model and provide the appropriate query method, or look at using a type class for this problem.
It seems the requirement is to use the companion object's method, but the issue with this solution is that, given this code:
trait RowData[B <: Model[B]]:
def query = B.query
Because B is supposed to be the companion object, it cannot be passed as a type in []. You need to pass it as an object in parentheses (), like this:
trait Model[A]:
def query = "model has this good stuff I want to use in RowData"
trait RowData(B: Model[_]):
def query = B.query
case class Person(name: String, age: Int) extends RowData(Person)
object Person extends Model[Person]
Person("onur", 38).query
However, this is not an ideal solution either, because B is not enforced to be the companion object you pass for the RowData trait. In Scala, we commonly use Context Bound to enforce this, like this:
trait Model[A]:
def query = "model has this good stuff I want to use in RowData"
trait RowData[B : Model]:
def query = summon[Model[B]].query
case class Person(name: String, age: Int) extends RowData[Person]
given Person: Model[Person] with {} // Implement custom query method here
Person("onur", 38).query

How to inherit class in scala [duplicate]

This question already has an answer here:
If case class inheritance is prohibited, how to represent this?
(1 answer)
Closed 5 years ago.
I am trying to inherit a class in Scala. My Parent class is
case class Person (name:String, age:Int, valid:Boolean)
My child class is
case class MarriedPerson (override val name: String,
override val age: Int,
override val valid: Boolean,
spouse: Person) extends Person(name, age, valid)
When I try this, I get an error saying
:13: error: case class MarriedPerson has case ancestor Person, but case-to-case inheritance is prohibited. To overcome this limitation, use extractors to pattern match on non-leaf nodes.
Why is this the case and how I get around this to get a case class to inherit another case class?
If I remove the "case" in the parent class, I get an error saying
that
:15: error: value name overrides nothing
override val name: String,
Why can't a case class not inherit from a normal class in this case?
You cannot inherit from one case class to another. In theory its possible to inherit from a case class to a non-case class, but that's not recommended (which is why it's a good practice to make your case classes final - more on the topic in this post).
Case classes have, among other peculiarities, that of exposing publicly all of the constructor arguments. This does not happen for normal classes, which is why to make your snippet of code work you should just prepend all of Person's arguments with val.
Thus, the following is valid syntax:
class Person(val name: String, val age: Int, val valid: Boolean)
case class MarriedPerson(
override val name: String,
override val age: Int,
override val valid: Boolean,
spouse: Person) extends Person(name, age, valid)
However, for your information, it is also quite common to use traits for this kind of situations, which allows for greater flexibility (traits support multiple inheritance) and a slightly less verbose syntax, as follows:
trait Person {
def name: String
def age: Int
def valid: Boolean
}
case class MarriedPerson(
name: String,
age: Int,
valid: Boolean,
spouse: Person) extends Person
If you are curious on why it's possible to define defs in traits that are overridden by vals in sub-classes, it's because Scala implements the Uniform Access Principle.

use dynamic return type in a scala fuction

I have a function that gets a string with name of model and returning an instance of that case class, but im wondering what is the correct way to use dynamic return type here in this function?
case class Person(fname: String, lname: String)
case class Animal(type: String, weight: Double)
def getInstanceOf(model: String): ??? = model match {
case "person" => Person("jack", "robinson")
case "animal" => Animal("lion", 190.0)
}
thanks!
It may help to have a bit more context of what the end goal is here to suggest a solution.
If your models all conform to a particular classification and you don't need to know exactly what instance they are then the abstract class (or trait) suggestion above should work.
EDIT: my original answer included this snippet:
You could also make the return type generic and put a type boundary on it in this scenario, such as:
def getInstanceOf[T <: Living](model: String): T = ...
But as pointed out below this allows the caller to specify what type they expect which may counteract what is contained in the model argument
If you only returned one of two possible types but needed the exact type then maybe an Either[T1, T2] would be appropriate here (http://www.scala-lang.org/api/current/scala/util/Either.html) such as:
def getInstanceOf(model: String): Either[Person, Animal] = ...
But again I think it would be easier to answer if the end goal was explained a bit
Maybe you can define an abstract class and extends both classes from it, and then you can specify the abstract class as the return type:
abstract class Living
case class Person(fname: String, lname: String) extends Living
case class Animal(Type: String, weight: Double) extends Living
def getInstanceOf(model: String): Living = model match {
case "person" => Person("jack", "robinson")
case "animal" => Animal("lion", 190.0)
}
// getInstanceOf: (model: String)Living

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]))