I shall explain my question with example as shown below.
import scala.reflect.ClassTag
trait LivingBeing extends Product { def name:String; def age:Int}
case class Person (name:String, age:Int) extends LivingBeing
case class Cat(name: String, age:Int) extends LivingBeing
// usual way of creating a case class instance
val john = Person("john", 23)
// Creating a case class instance with tuples
val garfield = Cat tupled ("Garfield", 8)
// create a generic function
def createLivingBeing[T<: LivingBeing](name:String, age:Int)(implicit evidence: ClassTag[T]): T = {
T tupled (name, age) // Does not compile; why?
}
How can one elegantly construct different case classes (that are of a certain trait) generically, given a type and values for its fields?
Consider type class solution
trait LivingBeingFactory[T <: LivingBeing] {
def apply(name: String, age: Int): T
}
object LivingBeingFactory {
implicit val personFactory: LivingBeingFactory[Person] =
(name: String, age: Int) => Person(name, age)
implicit val catFactory: LivingBeingFactory[Cat] =
(name: String, age: Int) => Cat(name, age)
}
def createLivingBeing[T <: LivingBeing](name:String, age:Int)(implicit evidence: LivingBeingFactory[T]): T = {
evidence(name, age)
}
createLivingBeing[Person]("Picard", 70)
// res0: Person = Person(Picard,70)
createLivingBeing[Cat]("Bob", 5)
// res1: Cat = Cat(Bob,5)
// Creating a case class instance with tuples
val garfield = Cat tupled ("Garfield", 8)
...which is the equivalent of...
val garfield = (Cat.apply _).tupled(("Garfield", 8))
This, on the other hand...
T tupled (name, age) // Does not compile; why?
...produces Error: not found: value T because T is a type, not a value. Cat is both a type and a value. It is the type specified for the class, but it is also the companion object to the class Cat. All case classes have a companion object with an apply() method. The compiler knows the difference between them and it knows where one can be used/referenced but not the other.
Related
Let's say I have case classes something like below.
trait Foo {
def a: String
}
case class Bar(a: String,b: Option[Int]) extends Foo{
def this(test: Test) = this(test.foo,None)
}
case class Buzz(a: String,b: Boolean) extends Foo{
def this(test: Test) = this(test.foo,false)
}
I'm using the constructor def this(test: Test) via reflection and working as I expected.
A method signature that I use the constructor is something like this
def test[T <: Foo: ClassTag](cb: (String) => Future[T]): Future[Result]
What I want to do is restrict that any case classes that extends trait Foo needs to have def this(test: Test).And the case if any of them don't have it, It should be a compile error.
My attempt
//Compile error
trait Foo[T] {
def a: String
def this(test: Test):T
}
Is there any way to do this?
Thanks in advance.
It is not possible to use the type system to enforce that a class has a specific constructor. This shouldn't really be a surprise, because you're already using reflection to access said constructor. Using a reflective call, the only way to check for the appropriate constructor would be to use more reflection--preferably via a macro to mail compilation fail.
There is almost always a better way than using reflection, though. In this case, we can use a type class to find the correct method that can build a sub-type of Foo (or anything, really) from a Test.
Let's assume Test looks like this:
case class Test(foo: String)
Then, we define a TestBuilder type class, which can provide evidence that we can build an A from a Test.
trait TestBuilder[A] {
def build(test: Test): A
}
// Convenience method for creating type class instances
object TestBuilder {
def apply[A](f: Test => A): TestBuilder[A] = new TestBuilder[A] {
def build(test: Test): A = f(test)
}
}
Then, we define out Foos, each with an instance of TestBuilder[A], where A is the type of each Foo:
trait Foo {
def a: String
}
case class Bar(a: String, b: Option[Int]) extends Foo
object Bar {
implicit val builder = TestBuilder(test => Bar(test.foo, None))
}
case class Buzz(a: String, b: Boolean) extends Foo
object Buzz {
implicit val builder = TestBuilder(test => Buzz(test.foo, false))
}
Note that we no longer need the alternate constructors, and rely on the type class instances to build our Foos using apply.
Now, your test method could look something like this. I changed around the return types because you don't define any implementation or what Result is, but the idea is the same.
def test[T <: Foo : ClassTag : TestBuilder](cb: String => Future[T]): Future[T] = {
val test = Test("abc")
// use the implicitly resolved type class to build `T` from a `Test`
val t = implicitly[TestBuilder[T]].build(test)
Future(t).andThen {
case Success(x) => cb(x.a)
}
}
Now, something like this will compile:
// T is Bar
scala> test((s: String) => Future(Bar(s, None)))
res0: scala.concurrent.Future[Bar] = scala.concurrent.impl.Promise$DefaultPromise#56f2bbea
And using some other type Baz, without an instance of TestBuilder[Baz] will fail.
case class Baz(a: String) extends Foo
scala> test((s: String) => Future(Baz(s)))
<console>:29: error: could not find implicit value for evidence parameter of type TestBuilder[Baz]
test((s: String) => Future(Baz(s)))
^
I don't think you can do quite what you're looking for. But maybe this would work for you:
trait Foo {
def a: String
def create(a: String): Foo
}
case class Bar(a: String,b: Option[Int]) extends Foo{
def create(a: String) = Bar(a,None)
}
case class Buzz(a: String,b: Boolean) extends Foo{
def create(a: String) = Buzz(a,false)
}
You would then have a way to construct a Bar or Buzz without having to specify the second parameter.
BTW, I didn't quite follow your template exactly because I didn't know what Test was supposed to be.
I don't think there's a way to do this directly. But one usually constructs case classes through factories -- their companion objects -- and that gives you the flexibility to do what you want in a different way.
Define
trait Test {
def foo : String = ???
}
abstract class Foo[T <: Foo[T]]()(implicit ev : FooMaker[T]) {
def a: String
}
trait FooMaker[T <: Foo[T]] {
def apply( test : Test ) : T
}
implicit object Bar extends FooMaker[Bar] {
def apply(test: Test) = Bar(test.foo,None)
}
case class Bar(a: String,b: Option[Int]) extends Foo[Bar]
implicit object Buzz extends FooMaker[Buzz] {
def apply(test: Test) = Buzz(test.foo,false)
}
case class Buzz(a: String,b: Boolean) extends Foo[Buzz]
But if you try to define a Foo without the factory method in the companion object that you require:
case class Barf(a : String, b : Short ) extends Foo[Barf]
You'll see
scala> case class Barf(a : String, b : Short ) extends Foo[Barf]
<console>:12: error: could not find implicit value for parameter ev: FooMaker[Barf]
case class Barf(a : String, b : Short ) extends Foo[Barf]
Add the companion object with the factory you need, and it's all good
implicit object Barf extends FooMaker[Barf] {
def apply(test: Test) = Barf(test.foo,0.toShort)
}
case class Barf(a : String, b : Short ) extends Foo[Barf]
In the REPL:
scala> :paste
// Entering paste mode (ctrl-D to finish)
implicit object Barf extends FooMaker[Barf] {
def apply(test: Test) = Barf(test.foo,0.toShort)
}
case class Barf(a : String, b : Short ) extends Foo[Barf]
// Exiting paste mode, now interpreting.
defined object Barf
defined class Barf
Note that to compile this stuff in the REPL you'll need to use :paste because the mutually interdependent definitions can't be defined separately.
Updated my question per feedback from commenters (Miles and m-z):
I'm finding duplicate "values" by either Name or Age.
sealed trait DuplicateResult
case class DuplicatesByName(name: String, people: Set[String]) extends DuplicateResult
case class DuplicatesByAge(age: Int, people: Set[String]) extends DuplicateResult
And the return type must be different depending on Name or Age:
sealed trait QueryByDuplicate {
type DuplicateResultType
}
case class Name(name: String) extends QueryByDuplicate {
override type DuplicateResultType = DuplicatesByName
}
case class Age(age: Int) extends QueryByDuplicate {
override type DuplicateResultType = DuplicatesByAge
}
Then, I define a function that compiles and runs:
def findDupes(x: QueryByDuplicate): DuplicateResult = x match {
case Name(n) => DuplicatesByName(n, Set("1", "2"))
case Age(a) => DuplicatesByAge(a, Set("42"))
}
scala> findDupes(Name("kevin"))
res0: DuplicateResult = DuplicatesByName(kevin,Set(1, 2))
scala> findDupes(Age(77))
res1: DuplicateResult = DuplicatesByAge(77,Set(42))
However, the type DuplicateResultType seems weak, since I could put any type there.
Please criticize and improve my implementation.
Option 1: The result type information is lost in your version because the caller cannot influence the fixed result type of findDupes. You can use generics so that the caller regains this control:
sealed trait QueryByDuplicate[T <: DuplicateResult]
case class Name(name: String) extends QueryByDuplicate[DuplicatesByName]
case class Age(age: Int) extends QueryByDuplicate[DuplicatesByAge]
def findDupes[T <: DuplicateResult](x: QueryByDuplicate[T]): T = x match {
case Name(n) => DuplicatesByName(n, Set("1", "2"))
case Age(a) => DuplicatesByAge(a, Set("42"))
}
val dupes: DuplicatesByName = findDupes(Name("kevin"))
Option 2: Since you asked for criticism too, I don't think the way you designed things is a good practice. Having several classes defining your queries, and a list of implementations for each class at a different place is duplicity hard to maintain. You could just use good old polymorphism:
sealed trait QueryByDuplicate[T <: DuplicateResult] {
def findDupes: T
}
class Name(name: String) extends QueryByDuplicate[DuplicatesByName] {
override def findDupes: DuplicatesByName = DuplicatesByName(name, Set("1", "2"))
}
class Age(age: Int) extends QueryByDuplicate[DuplicatesByAge] {
override def findDupes: DuplicatesByAge = DuplicatesByAge(age, Set("42"))
}
val dupes: DuplicatesByName = new Name("kevin").findDupes
This is how you would probably do it in Java. Sometimes it's better to stick to good old ways even when we have new toys.
Option 3: Talking about instanceOf, this also works:
sealed trait QueryByDuplicate {
type DuplicateResultType
}
case class Name(name: String) extends QueryByDuplicate {
override type DuplicateResultType = DuplicatesByName
}
case class Age(age: Int) extends QueryByDuplicate {
override type DuplicateResultType = DuplicatesByAge
}
def findDupes(x: QueryByDuplicate): x.DuplicateResultType = x match {
case Name(n) => DuplicatesByName(n, Set("1", "2")).asInstanceOf[x.DuplicateResultType]
case Age(a) => DuplicatesByAge(a, Set("42")).asInstanceOf[x.DuplicateResultType]
}
val dupes: DuplicatesByName = findDupes(Name("kevin"))
findDupes() will have a return type of Object in the bytecode, but Scala is smart enough to infer the correct type, apparently.
Given the following classes:
case class AddRequest(x: Int, y: Int)
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String)
case class ToUppercaseResponse(upper: String)
How do I define in a typesafe manner some function:
def process(req: ???): ???
Such that the following should hold true:
val r1: AddResponse = process(AddRequest(2, 3))
val r2: ToUppercaseResponse = process(ToUppercaseRequest("aaa"))
Also, the following should not compile:
val r3 = process("somestring")
This is both entirely possible and a totally reasonable thing to do in Scala. This kind of thing is all over Shapeless, for example, and something similar (but less principled) is the basis of the magnet pattern that shows up in Spray, etc.
Update: note that the following solution assumes that "given the following classes" means you don't want to touch the case classes themselves. If you don't care, see the second part of the answer below.
You'd want a type class that maps input types to output types:
case class AddRequest(x: Int, y: Int)
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String)
case class ToUppercaseResponse(upper: String)
trait Processable[In] {
type Out
def apply(in: In): Out
}
And then some type class instances:
object Processable {
type Aux[I, O] = Processable[I] { type Out = O }
implicit val toUppercase: Aux[ToUppercaseRequest, ToUppercaseResponse] =
new Processable[ToUppercaseRequest] {
type Out = ToUppercaseResponse
def apply(in: ToUppercaseRequest): ToUppercaseResponse =
ToUppercaseResponse(in.str.toUpperCase)
}
implicit val add: Aux[AddRequest, AddResponse] =
new Processable[AddRequest] {
type Out = AddResponse
def apply(in: AddRequest): AddResponse = AddResponse(in.x + in.y)
}
}
And now you can define process using this type class:
def process[I](in: I)(implicit p: Processable[I]): p.Out = p(in)
Which works as desired (note the appropriate static types):
scala> val res: ToUppercaseResponse = process(ToUppercaseRequest("foo"))
res: ToUppercaseResponse = ToUppercaseResponse(FOO)
scala> val res: AddResponse = process(AddRequest(0, 1))
res: AddResponse = AddResponse(1)
But it doesn't work on arbitrary types:
scala> process("whatever")
<console>:14: error: could not find implicit value for parameter p: Processable[String]
process("whatever")
^
You don't even have to use a path dependent type (you should be able just to have two type parameters on the type class), but it makes using process a little nicer if e.g. you have to provide the type parameter explicitly.
Update: everything above assumes that you don't want to change your case class signatures (which definitely isn't necessary). If you are willing to change them, though, you can do this a little more concisely:
trait Input[Out] {
def computed: Out
}
case class AddRequest(x: Int, y: Int) extends Input[AddResponse] {
def computed: AddResponse = AddResponse(x + y)
}
case class AddResponse(sum: Int)
case class ToUppercaseRequest(str: String) extends Input[ToUppercaseResponse] {
def computed: ToUppercaseResponse = ToUppercaseResponse(str.toUpperCase)
}
case class ToUppercaseResponse(upper: String)
def process[O](in: Input[O]): O = in.computed
And then:
scala> process(AddRequest(0, 1))
res9: AddResponse = AddResponse(1)
scala> process(ToUppercaseRequest("foo"))
res10: ToUppercaseResponse = ToUppercaseResponse(FOO)
Which kind of polymorphism (parametric or ad-hoc) you should prefer is entirely up to you. If you want to be able to describe a mapping between arbitrary types, use a type class. If you don't care, or actively don't want this operation to be available for arbitrary types, using subtyping.
You can define a common trait for Requests, and a common trait for Responses where the request type is defined for specific response type:
trait Request[R <: Response]
trait Response
case class AddRequest(x: Int, y: Int) extends Request[AddResponse]
case class AddResponse(sum: Int) extends Response
case class ToUppercaseRequest(str: String) extends Request[ToUppercaseResponse]
case class ToUppercaseResponse(upper: String) extends Response Response[ToUppercaseRequest]
Then, process signature would be:
def process[A <: Request[B], B <: Response](req: A): B
When you call process, you'll have to explicitly define the types so that the returned type is what you expect it to be - it can't be inferred specifically enough:
val r1: AddResponse = process[AddRequest, AddResponse](AddRequest(2, 3))
val r2: ToUppercaseResponse = process[ToUppercaseRequest, ToUppercaseResponse](ToUppercaseRequest("aaa"))
Let's say someone provided a function:
def getTupleData[T](source: String): List[T] = {
// ...
}
I need to write a function which takes a case class C as the type parameter and return List[C] with the help of the above function. Here is what I have got so far:
def getCaseClassData[C](source: String): List[C] = {
// Somehow get T from C.
// For example, if C is case class MyCaseClass(a: Int, b: Long), then T is (Int, Long)
// How to get T?
getTupleData[T](source) map { tuple: T =>
// Somehow convert tuple into a case class instance with the case class type parameter
// C.tupled(tuple) ?? Type parameter cannot be used like this. :(
}
}
More specifically, it seems to me I'm asking two questions here:
How to explicitly obtain the type of the tuple from a type parameter which represents a case class so that it can be used as a type parameter?
How to create a case class instance from a tuple instance without knowing the actual name of the case class but only a type parameter?
You won't find any reasonably simple or direct way to do it. If you' re ready for the more involved solutions, bear with me.
Every case class has an apply method in its companion object, which instantiates the class. By calling tupled on this method (after eta-expansion), you'll get a function that takes a tuple and creates the corresponding case class instance.
Now of course the problem is that the every case class's apply has a different signature. We can get around this by introducing a type class representing a case class factory, and provide instances of this type class through a macro (which will just delegate to the case class's apply method).
import scala.reflect.macros.whitebox.Context
import scala.language.experimental.macros
trait CaseClassFactory[C,T]{
type Class = C
type Tuple = T
def apply(t: Tuple): C
}
object CaseClassFactory {
implicit def factory1[C,T]: CaseClassFactory[C,T] = macro factoryImpl[C,T]
implicit def factory2[C]: CaseClassFactory[C,_] = macro factoryImpl[C,Nothing]
def apply[C,T]: CaseClassFactory[C,T] = macro factoryImpl[C,T]
def apply[C]: CaseClassFactory[C,_] = macro factoryImpl[C,Nothing]
def factoryImpl[C:c.WeakTypeTag,T:c.WeakTypeTag](c: Context) = {
import c.universe._
val C = weakTypeOf[C]
val companion = C.typeSymbol.companion match {
case NoSymbol => c.abort(c.enclosingPosition, s"Instance of $C has no companion object")
case sym => sym
}
val tupledTree = c.typecheck(q"""($companion.apply _).tupled""")
val T = tupledTree.tpe match {
case TypeRef(_, _, List(argTpe, _)) => argTpe
case t => c.abort(c.enclosingPosition, s"Expecting type constructor (Function1) for $C.tupled, but got $t: ${t.getClass}, ${t.getClass.getInterfaces.mkString(",")}")
}
if (! (c.weakTypeOf[T] <:< T)) {
c.abort(c.enclosingPosition, s"Incompatible tuple type ${c.weakTypeOf[T]}: not a sub type of $T")
}
q"""
new CaseClassFactory[$C,$T] {
private[this] val tupled = ($companion.apply _).tupled
def apply(t: Tuple): $C = tupled(t)
}
"""
}
}
With it you can do something like this:
scala> case class Person(name: String, age: Long)
defined class Person
scala> val f = CaseClassFactory[Person]
f: CaseClassFactory[Person]{type Tuple = (String, Long)} = $anon$1#63adb42c
scala> val x: f.Tuple = ("aze", 123)
x: f.Tuple = (aze,123)
scala> implicitly[f.Tuple =:= (String, Long)]
res3: =:=[f.Tuple,(String, Long)] = <function1>
scala> f(("aze", 123))
res4: Person = Person(aze,123)
But more importantly, you can require an instance of CaseClassFactory as an implicit parameter, allowing to generically instantiate your case classes. You can then do something like:
scala> implicit class TupleToCaseClassOps[T](val t: T) extends AnyVal {
| def toCaseClass[C](implicit f: CaseClassFactory[C,T]): C = {
| f(t)
| }
| }
defined class TupleToCaseClassOps
scala> case class Person(name: String, age: Long)
defined class Person
scala> ("john", 21).toCaseClass[Person]
res5: Person = Person(john,21)
Pretty neat. Armed with this type class, getCaseClassData then becomes:
def getCaseClassData[C](source: String)(implicit f: CaseClassFactory[C,_]): List[C] = {
getTupleData[f.Tuple](source) map { tuple: f.Tuple =>
f(tuple)
}
}
If I have a class C defined as
class C[A]
is there any way to create a new instance of A within C? Something like
class C[A] {
def f(): A = new A()
}
I understand that, if this were possible, you'd probably have to specify the constructor arguments somewhere, and that's fine.
If it's not possible, are there any design patterns for dealing with the sort of situation where you'd like to create a new instance of a type?
You could use a type class to abstract instantiation:
trait Makeable[T] {
def make: T
}
class C[T: Makeable] {
def f(): T = implicitly[Makeable[T]].make
}
For example,
implicit object StringIsMakeable extends Makeable[String] {
def make: String = "a string"
}
val c = new C[String]
c.f // == "a string"
When you instantiate C, you'll need to provide, explicitly or implicitly, a Makeable that will act as a factory of the appropriate type. That factory, of course, would be responsible for supplying any constructor arguments when it invokes the constructor.
Alternatively, you could use a Manifest, but be warned that this approach relies on reflection and is not type safe:
class C[T: Manifest] {
def f(): T = manifest[T].erasure.newInstance.asInstanceOf[T]
}
For completeness, you can also easily extend this approach to pass some or all of the constructor parameters in to the make method:
trait Makeable[Args, T] { def make(a: Args): T }
class C[Args, T](implicit e: Makeable[Args, T]) {
def f(a: Args): T = e.make(a)
}
// some examples
case class Person(firstName: String, lastName: String)
implicit val personFactory1 = new Makeable[(String, String), Person] {
def make(a: (String, String)): Person = Person(a._1, a._2)
}
implicit val personFactory2 = new Makeable[String, Person] {
def make(a: String): Person = Person(a, "Smith")
}
val c1 = new C[String, Person]
c1.f("Joe") // returns Person("Joe", "Smith")
val c2 = new C[(String, String), Person]
c2.f("John", "Smith") // returns Person("John", "Smith")
You can demand an implicit parameter, like so:
class A[T](implicit newT : T) {
val t = newT
}
All you need then is to have an implicit factory of the desired type in scope when you instanciate A, e.g. the following works:
implicit def newSeq[T] = Seq[T]()
val a = new A[Seq[String]]
As shown by:
scala> a.t
res22: Seq[String] = List()
The same as #Raphael's answer with a case class's apply method:
class Container[A](contained: A)
case class Person(name: String)
case class PersonContainer(person: Person) extends Container[Person](person)
implicit def _ = PersonContainer.apply _
class Creator {
def deserializeAndPackage[A, B <: Container[A]](data: Array[Byte])
(implicit containerCreator: (A => B)): B = {
val p = /* deserialize data as type of A */
containerCreator(p)
}
}