Method polymorphism - scala

I am trying to write a generic method f[T](id:String) that is something like this:
case class A(x:String)
case class B(y:String)
case class C(z:String)
def f[T](id:String): T = { /* equivalent to T(id) */ }
val result1:A = f[A]("123") // returns A("123")
val result2:B = f{B]("345") // returns B("345")
val result3:C = f[C]("567") // returns C("567")
Unfortunately I cannot figure out how to work with the type T inside the method, besides using reflection. By "working with the type T" i mean for example being able to do something like the following, which I know doesn't work (for illustration purposes only):
T match {
case A => A(id)
case B => B(id)
}
or simply invoke T(ID) to create a new object of whatever type T is.
I can of course break up this into three methods:
def f1(id:String): A = { A(id) }
def f2(id:String): B = { B(id) }
def f3(id:String): C = { C(id) }
val result1:A = f1("123") // returns A("123")
val result2:B = f2("345") // returns B("345")
val result3:C = f3("567") // returns C("567")
but I'm hoping there is a way to keep it as one generic method to avoid some ugly boilerplate code duplication, and still be nearl as fast as the tree method version.

If you do not want to use reflection (ClassTag or TypeTag), you could use a Factory type class to achieve the desired functionality (unless it defeats the purpose of your generic function by generating a lot of duplicated simple code ;)).
case class A(s: String)
case class B(s: String)
case class C(s: String)
trait Factory[T] extends ((String) => T) {
def apply(arg: String): T
}
object Factory {
implicit object AFactory extends Factory[A] {
override def apply(arg: String): A = A(arg)
}
implicit object BFactory extends Factory[B] {
override def apply(arg: String): B = B(arg)
}
implicit object CFactory extends Factory[C] {
override def apply(arg: String): C = C(arg)
}
}
def create[T : Factory](arg: String): T = implicitly[Factory[T]].apply(arg)
create[A]("foo") | -> res0: A = A(foo)

Related

Is there some way in scala that I can return a type?

I have a lot of classes such as DataFrameFlow, TextFlow, RDDFlow. They all derive from base class Flow.
Now I want to write a function judgeFlow which can read from a path: String and return something representing exact Flow type from which I can create corresponding instance. The whole code seems like the following
def judgeFlow(path:String) = /*1*/ {
Flow.getStoreType(path) match {
case StoreType.tdw =>
DataFrameFlow
case StoreType.hdfs =>
TextFlow
}
}
def createFlow(typeInfo:/*2*/) = /*3*/{
new typeInfo()
}
However, I don't know how to write in place 1, 2 and 3.
EDIT
Knowing how to construct them is not enough here, because I also want the following:
pattern matching through typeInfo
some ways to do asInstanceOf
EDIT 2
Definition of Flow
abstract class Flow(var outputName: String) extends Serializable{
def this() = this("")
...
}
Definition of DataFrameFlow
class DataFrameFlow(d: DataFrame, path: String) extends Flow {
var data: DataFrame = d
def this(data: DataFrame) = this(data, "")
def this(path: String) = this(null, path)
def this() = this(null, "")
...
}
Pattern matching can't return different types from different cases. The type returned by pattern matching is the least upper bound of types returned in cases.
When someone wants to return different types, most probably he/she wants a type class.
sealed abstract class Flow
class DataFrameFlow extends Flow
class TextFlow extends Flow
class RDDFlow extends Flow
trait JudgeFlow[In] {
type Out <: Flow
def judgeFlow(in: In): Out
}
object JudgeFlow {
implicit val `case1`: JudgeFlow[???] { type Out = DataFrameFlow } = ???
implicit val `case2`: JudgeFlow[???] { type Out = TextFlow } = ???
implicit val `case3`: JudgeFlow[???] { type Out = RDDFlow } = ???
}
def judgeFlow[In](in: In)(implicit jf: JudgeFlow[In]): jf.Out = jf.judgeFlow(in)
But the trouble is that types are resolved at compile time. You seem to want to choose a case based on a value of string i.e. at runtime. So you can't return more specific types than just Flow at compile time.
flatMap with Shapeless yield FlatMapper not found
It's hard to guess your use case completely.
But using Scala reflection you can try
import scala.reflect.runtime.universe._
import scala.reflect.runtime.currentMirror
def judgeFlow(path:String): Type = {
Flow.getStoreType(path) match {
case StoreType.tdw =>
typeOf[DataFrameFlow]
case StoreType.hdfs =>
typeOf[TextFlow]
}
}
def createFlow(typeInfo: Type): Flow = {
val constructorSymbol = typeInfo.decl(termNames.CONSTRUCTOR).asMethod
val classSymbol = typeInfo.typeSymbol.asClass
val classMirror = currentMirror.reflectClass(classSymbol)
val constructorMirror = classMirror.reflectConstructor(constructorSymbol)
constructorMirror().asInstanceOf[Flow]
}

returning different return types in scala

Is there a way to return different return types from a single method in scala?
For example, if I have a load() method, I would like to return different data types depending on the object that called this method.
def load(path: String):<return type>
{
// if this instance is of type "type1", do some processing on this object,
// and return object of type "type1"
// else if this instance is of type "type2", do some processing on this object,
// and return object of type return "type2"
}
If I understand correctly what you want, F-bounded polymorphism may work for you:
trait Base[T <: Base[T]] {
def load(path: String): T
}
class Type1 extends Base[Type1] {
override def load(path: String): Type1 = new Type1 // provisional implementation
}
class Type2 extends Base[Type2] {
override def load(path: String): Type2 = new Type2
}
Then load will return the type of the current object. Note the result types in those expressions:
new Type1().load(path)
scala> res2: Type1 = Type1#744f0e0b
new Type2().load(path)
scala> res3: Type2 = Type2#77988c45
You can use scala's Either:
def load(path : String) : Either[Type1, Type2] = {
this match {
case t1 : Type1 => Left(someProcessing(t1))
case t2 : Type2 => Right(someOtherProcessing(t2))
}
}
As others stated, you can use
You can use scala's Either
just keep in mind that every method that invokes your method, will need to check which of the types it is returning (using .map or pattern matching). Either is usually used like Either[ErrorType, NormalType] btw, but of course you can use it however you want
scala cats library has other alternative: http://eed3si9n.com/herding-cats/Xor.html
and of course, scalaz also provides an alternative: http://appliedscala.com/blog/2016/scalaz-disjunctions/
As a last resort, don't you can define your own "Either"
If your requirement is as simple as returning some instance of the type of context instance... ie this then you can just do this,
class A() {
def omg(s: String): this.type = new A()
}
And if inheritence is involved,
trait A {
type omgType
def omg(s: String): omgType
}
class B() extends A {
override type omgType = this.type
override def omg(s: String): omgType = new B()
}
class C() extends A {
override type omgType = this.type
override def omg(s: String): omgType = new C()
}
But if you want more generality then you may want to read the following and apply it there,
The easiest way will be to take inspiration from the magnet pattern which was heavily used in Spray.
We can leverage the inspiration to build our custom solution, remember it is neither pure magnet pattern nor is path dependent type approach. Its a hacky cocktail of both.
So... lets say you want your def process to be able to support input parameters of type Int and String and finally return the respective result.
You will need to define implicit magnets for these types,
trait ProcessMagnet {
type Input
type Result
def input: Input
def process: Result
}
object ProcessMagnetProvider {
implicit def stringToStringProcessMagnet(string: String): ProcessMagnet = new ProcessMagnet {
override type Input = String
override type Result = String
override def input: Input = string
// define this for your doing...
override def process: Result = input + "_omg"
}
//... add for all your inputs
implicit def intToIntProcessMagnet(int: Int): ProcessMagnet = new ProcessMagnet {
override type Input = Int
override type Result = Int
override def input: Input = int
// define this for your doing...
override def process: Result = input + 1
}
}
def process[T](t: T)(implicit pmConverter: T => ProcessMagnet): ProcessMagnet = pmConverter(t)
// now just import our implicit magnets...
import ProcessMagnetProvider._
val intResult: Int = process(5).process.asInstanceOf[Int]
val stringResult: String = process("omg").process.asInstanceOf[String]
what about factory method and just defining a trait loadable eg:
trait Loadable {
def load(path: String): Loadable
}
class Type1 extends Loadable {
def load(path: String): Type1 = this
}
class Type2 extends Loadable {
def load(path: String): Type2 = this
}
object Main {
def test(): Loadable = {
new Type1().load("path")
}
def main(args: Array[String]): Unit = {
println(test().getClass)
}
}

Using mixin composition with functions in scala

I'm trying to use mixin composition using functions, but I have an error in the apply method of obj object:
Overriding method apply in trait t of type (s: String)String; method apply needs abstract override modifiers.
How to solve this error and which is the correct implementacion?
trait t extends Function1[String,String] {
abstract override def apply(s: String): String = {
super.apply(s)
println("Advice" + s)
s
}
}
object MixinComp {
def main(args: Array[String]) {
val obj = new Function1[String, String] with t {
override def apply(s: String) = s
}
println(obj.apply("Hi"))
}
}
Your immediate problem (the reason it complains about the error) is that you can't have an abstract call in your linearization flow (your t.apply calls super.apply, which is abstract).
Also, the apply method you define in the top level anonymous class overrides everything, and does not call super, making the t being mixed in completely irrelevant.
Something like this would solve both problems:
trait t extends Function1[String,String] {
abstract override def apply(s: String): String = {
println("Advice" + s)
super.apply(s) // I rearranged this a little, because it kinda makes more sense this wat
}
}
// Note, this extends `Function1`, not `t`, it, just a "vanilla" Function1
class foo extends Function1[String, String] {
def apply(s: String): String = s
}
// Now I am mixing in the t. Note, that the apply definition
// from foo is now at the bottom of the hierarchy, so that
// t.apply overrides it and calls it with super
val obj = new foo with t
obj("foo")
You won't need to use the abstract modifier in your t trait definition, if you don't call the super.apply. And in this particular case I dont see any need for calling super.apply as Function1's apply is abstract. You probably need custom apply implementations. The following code should work.
trait t extends Function1[String, String] {
override def apply(s: String): String = {
// super.apply(s)
println("Advice" + s)
s
}
}
Case1: use the overridden apply method in t trait:
val obj = new Function1[String, String] with t {}
obj.apply("hello") // prints: Advicehello
Case 2: override the apply method in t trait in an anonymous class:
val obj = new Function1[String, String] with t {
override def apply(s: String): String = s
}
obj.apply("hello") // prints hello

Creating instance type after filter

I have a simple class which extends IndexedSeq[MyType]
class MyClass(someName: String, values: Iterable[MyType]) extends IndexedSeq[MyType] {
val name = someName
val rows = values.toVector
....
}
This works fine, allowing me to call all the standard collection methods on this object, filter, map etc and returning me the results as an IndexedSeq[MyType].
What I would like is for the results be returned as a new instance of MyClass or a boilerplate free way of doing this so I don't require the additional manual step each time of creating a new MyClass. EG:
val results = myClassInstance.filter(t => t)
val newMyClass = new MyClass(myClassInstance.name, results)
Is there any way of simplifying and doing something like the following, given that I need access to the original myClassInstance to copy the name value from it to the new object.
val newMyClass = myClassInstance.filter(t => t).toMyClass
Thanks
I've figured out the solution by extending IndexedSeqLike[DataRow, DataView] and implementing a builder in the companion object. So far this seems to do exactly what I was after without needing passthough calls.
class MyClass(someName: String, values: Iterable[MyType])
extends IndexedSeq[MyType]
with IndexedSeqLike[MyType, MyClass] {
val name = someName
val rows = values.toVector
// Supply a builder method which will get used on filter, reverse etc,
override def newBuilder: mutable.Builder[MyType, MyClass] =
MyClass.newBuilder(name)
....
}
Then in the companion object, add the following...
object MyClass {
// Builder for a new MyClass instance.
def newBuilder(name: String): mutable.Builder[MyType, MyClass] =
Vector.newBuilder[MyType] mapResult (vector => new MyClass(name, vector))
}
Now the following work as required :
val filteredData: MyClass = myClass.filter(f => f)
val reversedData: MyClass = myClass.reverse
Are you sure you need to inherit from IndexedSeq?
If you need only filter & map function in your class, you can implement them, without overriding IndexedSeq
class MyClass(name: String, values: Iterable[MyType]) {
def filter(f: MyType => Boolean): MyClass = {
new MyClass(name, values.filter(f))
}
def map(f: MyType => MyType): MyClass = {
new MyClass(name, values.map(f))
}
}
or If you really really need to override IndexedSeq, you can add explicit filter method to your class
class MyClass(name: String, values: Iterable[MyType]) extends IndexedSeq[MyClass] {
def filterClass(f: MyType => Boolean): MyClass = {
new MyClass(name, values.filter(f))
}
}
Or another option
class MyClass(name: String, values: Iterable[MyType]) extends IndexedSeq[MyClass] {
def copyWith(f: Iterable[MyType] => Iterable[MyType]): MyClass = {
new MyClass(name, f(values))
}
}
MyClass.copyWith(_.filter(...))
MyClass.copyWith(_.filterNot(...))
MyClass.copyWith(_.map(...))

how to copy instance and override value field declared in trait

Suppose I have some abstract value field defined in a trait:
trait Base {
val toBeOverride: String
}
case class Impl(other:Int) extends Base {
override val toBeOverride = "some value"
}
How can I write a function that I can easily get a cloned instance only overriding the toBeOverride value, like this:
// copy only available to case class instance
// v does not have method 'copy'
def overrideBaseValue[T <: Base](v: Base) =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
?
Edit
#som-snytt, I don't think this is a duplicate, just like a Trait is not the same as an Abstract Class. And the answers of that question do not satisfy me, see below.
#Blaisorblade, yes, it is a problem. For instances of each sub case class, the toBeOverride field are the same, so it should not appear in the constructor.
For now all the suggestions are to define an customized copy method in each(!) sub case class and that in my opinion is ugly and shows the incapability of the language.
The simplest solution is to just add the method you want to Base:
trait Base {
val toBeOverride: String
def copyBase(newToBeOverridden: String): Base
}
case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
def copyBase(newToBeOverridden: String) = copy(toBeOverride = newToBeOverridden)
}
This also allows to directly create an instance of Impl while specifying the value of toBeOverride (which wasn't possible). The only disadvantage is that now pattern matches using Impl have to change syntax - please update your question and add a comment if that's a problem.
BTW, if you just want to add a prefix (as in your example), that's no problem:
case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
def copyBase(newToBeOverridden: String) = copy(toBeOverride = toBeOverride + newToBeOverridden)
}
Here are two mechanisms.
Apparently, in the near future you'll be able to write a macro that can emit the anonymous subclass, but until then, I think this typeclass is not arduous.
Just kicking the tires on Dynamic here.
import scala.language.dynamics
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
trait Base {
def m: String
}
case class Impl(p: Int) extends Base {
override val m = "some value"
}
trait Basic extends Dynamic {
protected def m: String
def selectDynamic(f: String): Any =
if ("m" == f) m else reflecting(this, f)
protected def reflecting(b: Basic, f: String) = {
val im = cm.reflect(b)
val member = im.symbol.typeSignature member newTermName(f)
require(member != NoSymbol, s"No such member $f")
(im reflectMethod member.asMethod)()
}
}
case class Implic(p: Int) extends Basic {
override protected val m = "some value"
}
object Test extends App {
implicit class Copy[A <: Base](val b: A) {
def overriding(overm: String): A = (b match {
case impl: Impl => new Impl(impl.p) { override val m = overm }
case b: Base => new Base { override val m = overm }
}).asInstanceOf[A]
}
implicit class Proxy[A <: Basic : ClassTag](val b: A) {
def proximately(overm: String): Basic = new Basic {
override val m = overm
override def selectDynamic(f: String): Any =
if ("m" == f) overm else reflecting(b, f)
override def toString = b.toString
}
}
// asked for this
//def overriding[T <: Base](v: Base) = v.copy(m = "prefix" + v.m)
/* want something like this
def overriding[T <: Base](v: Base) = new Impl(v.p) {
override val m = "some value"
} */
val a = Impl(5)
val b = a overriding "bee good"
Console println s"$a with ${a.m} ~> $b with ${b.m}"
// or
val c = Implic(7)
val d = c proximately "dynomite"
Console println s"$c with ${c.m} ~> $d with ${d.m}"
}
Since traits don't get copy methods automatically, you can try using a Base case class instead:
case class Base(toBeOverride: String)
case class Impl(other: Int, someVal: String = "some value") extends Base(someVal)
def overrideBaseValue[T <: Base](v: Base) =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
The problem that you're going to run into though, is that copy returns an instance of Base and I don't think that you can convert it back to your original Impl class. For instance, this won't compile:
def overrideBaseValue[T <: Base](v: T): T =
v.copy(toBeOverride = "prefix" + v.toBeOverride)