I have a generic method that can accept any tuple of any size, the only constraint is that the first element of this tuple should be of type MyClass.
Something like this:
trait MyTrait[T <: (MyClass, _*)] {
getMyClass(x: T): MyClass = x._1
}
I've tried this
trait MyTrait[T <: (MyClass, _) with (MyClass, _, _) with (MyClass, _, _) with ...] {
getMyClass(x: T): MyClass = x._1
}
but I get the error unboud wildcard type
If you want to do this without either boilerplate or runtime reflection, Shapeless is your best bet. You can use the IsComposite type class to put type-level constraints on the first element of a tuple:
import shapeless.ops.tuple.IsComposite
trait MustBeFirst
class MyClass[P <: Product](p: P)(implicit ev: IsComposite[P] { type H = MustBeFirst }) {
def getMustBeFirst(x: P): MustBeFirst = ev.head(p)
}
And then:
scala> val good2 = (new MustBeFirst {}, "")
good2: (MustBeFirst, String) = ($anon$1#7294acee,"")
scala> val good3 = (new MustBeFirst {}, "", 123)
good3: (MustBeFirst, String, Int) = ($anon$1#6eff9288,"",123)
scala> val good4 = (new MustBeFirst {}, "", 'xyz, 123)
good4: (MustBeFirst, String, Symbol, Int) = ($anon$1#108cdf99,"",'xyz,123)
scala> val bad2 = ("abc", 123)
bad2: (String, Int) = (abc,123)
scala> new MyClass(good2)
res0: MyClass[(MustBeFirst, String)] = MyClass#5297aa76
scala> new MyClass(good3)
res1: MyClass[(MustBeFirst, String, Int)] = MyClass#3f501844
scala> new MyClass(good4)
res2: MyClass[(MustBeFirst, String, Symbol, Int)] = MyClass#24e15478
scala> new MyClass(bad2)
<console>:15: error: could not find implicit value for parameter ev: shapeless.ops.tuple.IsComposite[(String, Int)]{type H = MustBeFirst}
new MyClass(bad2)
^
If you need to use a trait, you can put the ev (for "evidence") requirement inside the definition instead of in the constructor:
trait MyTrait[P <: Product] {
implicit def ev: IsComposite[P] { type H = MustBeFirst }
}
Now any class instantiating MyTrait will have to provide evidence that P is a tuple with MustBeFirst as its first element.
It's a little bit unsafe but you can use Structural type in this case:
trait MyTrait {
def getMyClass(x: {def _1: MyClass}): MyClass = x._1
}
Scala can't use generic tuple with unknown size because Products don's inherit themeselfs. You can try to use Shapeless or Products from play json lib.
This is now possible in Scala 3 and very straightforward:
class MyClass
trait MyTrait[T <: MyClass *: _] {
def getMyClass(x: T): MyClass = x.head
}
The *: infix operator is the type level equivalent of the +: sequence prepend (or :: for lists). So we essentially require type T to be a tuple such that its first member is of type MyClass. Note that the members of generic tuples cannot be retrieved using the usual _1, _2, ... attributes, but should be accessed using list-like methods (head, tail, apply, etc.). More surprising, these methods are type-safe since they carry precise type information.
Demo:
object Test1 extends MyTrait[(MyClass, Int, String)] // Compiles
//object Test2 extends MyTrait[(Int, String)] // Does not compile!
//object Test3 extends MyTrait[EmptyTuple] // Neither does this
val myClass = Test1.getMyClass((new MyClass, 1, "abc"))
summon[myClass.type <:< MyClass] // Compiles
Read more about match types, type inference.
You need to inherit your trait from Product, through which you can have productIterator, productArity and, productElement to handle the returned value. Here is an example
case class MyClass()
trait MyTrait[T <: Product] {
def getMyClass(x: T): Option[MyClass] =
if(
x.productIterator.hasNext
&&
x.productIterator.next().isInstanceOf[MyClass]
){
Some(x.productIterator.next().asInstanceOf[MyClass])
} else {
None
}
}
case class Test() extends MyTrait[Product]
And you can invoke like this
Test().getMyClass((MyClass(), 1,3,4,5))
//res1: Option[MyClass] = Some(MyClass())
Test().getMyClass((1,3,4,5))
//res2: Option[MyClass] = None
Hope this helps you.
If you are looking for compile time guarantee then this is one of the use cases for
Shapeless,
You need to add Shapeless in your build.sbt,
libraryDependencies ++= Seq("
com.chuusai" %% "shapeless" % "2.3.3"
)
Now, you can use Shapeless to define a typesafe getter which comes with compile time guarantees,
scala> import shapeless._
// import shapeless._
scala> import ops.tuple.IsComposite
// import ops.tuple.IsComposite
scala> import syntax.std.tuple._
// import syntax.std.tuple._
scala> case class Omg(omg: String)
// defined class Omg
scala> val myStringTuple = ("All is well", 42, "hope")
// myStringTuple: (String, Int, String) = (All is well,42,hope)
scala> val myOmgTuple = (Omg("All is well"), 42, "hope")
// myOmgTuple: (Omg, Int, String) = (Omg(All is well),42,hope)
Now if you want to enrich your tuples with a "first" getter with a specific type then,
scala> implicit class GetterForProduct[B <: Product](b: B) {
| def getFirst[A](implicit comp: IsComposite[B] { type H = A }): A = b.head
| }
// defined class GetterForProduct
scala> val myString = myStringTuple.getFirst[String]
// myString: String = All is well
scala> val myOmgError = myOmgTuple.getFirst[String]
// <console>:24: error: could not find implicit value for parameter comp: shapeless.ops.tuple.IsComposite[(Omg, Int, String)]{type H = String}
// val myOmgError = myOmgTuple.getFirst[String]
// ^
scala> val myOmg = myOmgTuple.getFirst[Omg]
// myOmg: Omg = Omg(All is well
If you don't need the implicit enrichment and are just looking for a way to "lock" the type in a getter and use it for corresponding types,
scala> trait FirstGetterInProduct[A] {
| def getFirst[B <: Product](b: B)(implicit comp: IsComposite[B] { type H = A }): A = b.head
| }
// defined trait FirstGetterInProduct
scala> object firstGetterInProductForString extends FirstGetterInProduct[String]
// defined object firstGetterInProductForString
scala> object firstGetterInProductForOmg extends FirstGetterInProduct[Omg]
// defined object firstGetterInProductForOmg
// Right tuple with right getter,
scala> val myString = firstGetterInProductForString.getFirst(myStringTuple)
// myString: String = All is well
// will fail at compile time for tuple with different type for first
scala> val myOmgError = firstGetterInProductForString.getFirst(myOmgTuple)
// <console>:23: error: could not find implicit value for parameter comp: shapeless.ops.tuple.IsComposite[(Omg, Int, String)]{type H = String}
// val myOmgError = firstGetterInProductForString.getFirst(myOmgTuple)
// ^
scala> val myOmg = firstGetterInProductForOmg.getFirst(myOmgTuple)
// myOmg: Omg = Omg(All is well)
Related
I'm trying to implement a convenient generic field accessor based on LabelledGeneric.
The usage should look like:
case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)
val foo: Foo = ???
val bar: Bar = ???
val uhuGenField = new GenField('uhu)
val uhuFooAccess = uhuGenField.from[Foo]
val uhuBarAccess = uhuGenField.from[Bar]
def someFunWithUhu[X](xs: Seq[X], access: uhuGenField.Access[X]) = ???
I spent some time trying to figure out how to achieve such behaviour.
Eventually I came up with this approach:
import shapeless._
import shapeless.ops.record.Selector
final class GenField[V](val fieldName: Symbol) {
val fieldWitness = Witness(fieldName)
type FieldNameType = fieldWitness.T
trait Access[C] {
def get(c: C): V
}
def from[C](implicit lg2hl: LGtoHL[C]): Access[C] = new Access[C] {
override def get(c: C): V = {
val labelledGeneric = lg2hl.labelledGeneric
val selector = Selector.mkSelector[labelledGeneric.Repr, FieldNameType, V]
selector(labelledGeneric.to(c))
}
}
}
// I need something like this to enable syntax like
// genField.from[DesiredClass]
// i.e. to "latch" Repr inside a single instance
// and to don't pass it explicitly to `from` method.
sealed trait LGtoHL[A] {
type Repr <: HList
val labelledGeneric: LabelledGeneric.Aux[A, Repr]
}
object LGtoHL {
implicit def mkLGtoHL[A, ARepr <: HList](implicit lg: LabelledGeneric.Aux[A, ARepr]): LGtoHL[A] = {
new LGtoHL[A] {
override type Repr = ARepr
override val labelledGeneric: LabelledGeneric.Aux[A, Repr] = lg
}
}
}
From my prospective this solution should be OK, but it still doesn't work.
The compilation fails with the following error message:
Error:(17, 41) lg2hl.Repr is not an HList type
val selector = Selector.mkSelector[labelledGeneric.Repr, FieldNameType, V]
Why does it complain lg2hl.Repr is not an HList type?
Repr is explicitly defined in LGtoHL as type Repr <: HList.
What is wrong with my code?
Very appreciate your help.
Why are lenses not enough?
import shapeless.{Lens, lens}
case class Foo(aha: String, uhu: Double, ehe: Int)
case class Bar(uhu: Double, ahu: Boolean)
val foo: Foo = Foo("a", 1.0, 2)
val bar: Bar = Bar(3.0, true)
val fooUhu: Lens[Foo, Double] = lens[Foo] >> 'uhu
val barUhu: Lens[Bar, Double] = lens[Bar] >> 'uhu
fooUhu.get(foo) // 1.0
barUhu.get(bar) // 3.0
The error message
lg2hl.Repr is not an HList type
comes from here: https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/generic.scala#L511
u.baseType(HConsSym) is now NoType.
I guess GenField[V](val fieldName: Symbol) will not work since fieldName in Witness(fieldName) must be known at compile time. For example
lens[Foo] >> 'uhu
works but
val uhu: Witness.`'uhu`.T = 'uhu.narrow
lens[Foo] >> uhu
doesn't. This is the reason why lenses, Witness, LabelledGeneric are implemented via macros.
given a string (say "345435.454") and another string (say "java.lang.Double") at runtime , how can I create a object of class/Type java.lang.Double with value given in first argument.
Using the second argument I can get the class and the type but I'm unable to get the TypeTag with generic Type "T" as java.lang.Double
scala>def getType[T](clazz: Class[T])(implicit runtimeMirror: ru.Mirror) =
| runtimeMirror.classSymbol(clazz).toType
getType: [T](clazz: Class[T])(implicit runtimeMirror: reflect.runtime.universe.Mirror)reflect.runtime.universe.Type
scala> def cast[A](a:Any,tt: TypeTag[A]):A = a.asInstanceOf[A]
cast: [A](a: Any, tt: reflect.runtime.universe.TypeTag[A])A
-- from type to typetag
scala>def backward[T](tpe: Type): TypeTag[T] =
TypeTag(mirror, new api.TypeCreator {
def apply[U <: api.Universe with Singleton](m: api.Mirror[U]) =
if (m eq mirror) tpe.asInstanceOf[U # Type]
else throw new IllegalArgumentException(s"Type tag defined in $mirror cannot be migrated to other mirrors.")
})
scala>def sToI(s:String) = Integer.valueOf(s)
scala>def parseDouble(s: String) = s.toDouble
scala>val conversionMap = Map("java.lang.Integer" -> sToI , "java.lang.Double" -> parseDouble _ )
// I have to provide typetag for java.lang.Double and hardcode it to get Double object
scala> cast(conversionMap("java.lang.Double")("345435.454"),typeTag[java.lang.Double])
res185: Double = 345435.454
// If try to get the typetag by using type from className , it doesn't work because I get typeTag[Nothing] => TypeTag[java.lang.Double]. backward is the function to get typetag from type
scala>backward(getType(Class.forName("java.lang.Double")))
res186: reflect.runtime.universe.TypeTag[Nothing] = TypeTag[Double]
Is there any way to get the reflect.runtime.universe.TypeTag[Double] = TypeTag[Double], with out providing the generic Type ?
update to comment as to what I'd do with the above.
scala> case class ArbObject[T](args:Array[String]) { def construct()(implicit m : Manifest[T]) : T ={ val const = m.runtimeClass.getConstructors()(0)
| const.newInstance(args:_*).asInstanceOf[T]
| }}
defined class ArbObject
scala> case class Foo(str1:String,str2:String)
defined class Foo
scala> (new ArbObject[Foo](Array("hello","world"))).construct()
res0: Foo = Foo(hello,world)
scala> case class Foo1(int1:Integer,str2:String)
defined class Foo1
Suppose I want every instance of an Item to be a SourceOf that Item, and every SourceOf a subtype of Item to be a SourceOf all instances of that subtype. This seems like a natural way to do it:
object Demo {
trait Item extends SourceOf[this.type]
trait SourceOf[+A <: Item]
}
Obviously this won't work, since this refers to the Demo object, not to each Item instance. (I've tested it.)
How can I tell the Scala compiler that every Item is a source of itself? It should be possible to get the compiler to deduce stuff like:
"Every source of (all) farming implements is a source of the specific farming implement you're looking for."
"If you're looking for the source of a farming implement, and you already have the farming implement, then you need look no further."
Here's an example (written to ignore type erasure for brevity):
trait FarmingImplement extends Item
object TheRototiller extends FarmingImplement
def findSource(item: Item, seq: Seq[SourceOf[_]]): Option[SourceOf[item.type]] =
seq.collectFirst {
case src: SourceOf[item.type] => src
}
val sources = Seq( // example: possible sources of TheRototiller
new SourceOf[Nothing] { override def toString = "Wrong source" },
new SourceOf[FarmingImplement] { override def toString = "Right" },
TheRototiller /* also right */ )
val got = findSource(TheRototiller, sources).get
println(got) // Should print "Right", since the second source in `sources`
// is the first match.
I want the type of got be SourceOf[TheRototiller.type], not SourceOf[Item]. But mainly I want Scala's type system to do the work of determining whether a SourceOf matches a given Item or category of Items.
At least, this gonna solve your problem with passing this:
object Demo {
trait C[+A]
trait Item extends SourceOf {
type T = C[this.type]
}
trait SourceOf {
type T <: C[Item]
}
}
Example (you'll have to compare Ts instead of SourceOfs here):
scala> val i = new Item{}
i: Demo.Item = $anon$1#c70cb4c
scala> implicitly[i.T =:= i.T] //compile-time type equality check
res4: =:=[i.T,i.T] = <function1>
scala> val i2 = new Item{}
i2: Demo.Item = $anon$1#1f26ac06
scala> implicitly[i.T =:= i2.T]
<console>:18: error: Cannot prove that i.T =:= i2.T.
implicitly[i.T =:= i2.T]
If you need to check types in runtime (as you've shown in example with pattern matching) - I would recomend to use TypeTag:
import scala.reflect.runtime.universe._
object Demo {
abstract class C[+A: TypeTag]
trait Item extends SourceOf {
type T = C[this.type]
}
trait SourceOf {
val tag = typeTag[this.type] //just to access
}
}
Usage:
scala> val i = new Item{}
i: Demo.Item = $anon$1#3b2111b0
scala> val i2 = new Item{}
i2: Demo.Item = $anon$1#4e7bb48a
scala> typeTag[i.T].tpe =:= typeTag[i2.T].tpe //runtime type-equality check
res9: Boolean = false
scala> typeTag[i.T].tpe =:= typeTag[i.T].tpe
res10: Boolean = true
Finally, implementation of findSource:
import scala.reflect.runtime.universe._
object Demo {
trait Item extends SourceOf
trait SourceOf {
type I = this.type
val tag = typeTag[I]
}
def findSource(item: Item, seq: Seq[SourceOf]): Option[item.I] =
seq.find(_.tag.tpe =:= item.tag.tpe).asInstanceOf[Option[item.I]]
}
Examples:
scala> val i = new Item{}
i: Demo.Item = $anon$1#1fc2899d
scala> val i2 = new Item{}
i2: Demo.Item = $anon$1#5f308abb
scala> val ri = findSource(i, Seq(i, i2)).get
ri: i.I = $anon$1#1fc2899d
scala> implicitly[ri.I =:= i.I]
res2: =:=[ri.I,i.I] = <function1>
asInstanceOf is used (it's safe to do here) because Seq looses path-dependent type in compile-time and we used runtime-check to match types, so no way to find it for compiler. However, Shapeless2's HList + Selector based implementation might be more pure.
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)
}
}
Is it possible to do something like this in Scala:
class MyTest {
def foo[A <: String _or_ A <: Int](p:List[A]) = {}
}
That is, the type A could be a String or Int. Is this possible?
(Similar question here)
Not really possible as you put it, but you can do it using the type class pattern. For example, from here:
sealed abstract class Acceptable[T]
object Acceptable {
implicit object IntOk extends Acceptable[Int]
implicit object LongOk extends Acceptable[Long]
}
def f[T: Acceptable](t: T) = t
scala> f(1)
res0: Int = 1
scala> f(1L)
res1: Long = 1
scala> f(1.0)
<console>:8: error: could not find implicit value for parameter ev: Acceptable[Double]
f(1.0)
^
EDIT
This works if class and object are companions. On REPL, if you type each on a different line (ie, a "result" appears between them), they are not companions. You can type it like below, though:
scala> sealed abstract class Acceptable[T]; object Acceptable {
| implicit object IntOk extends Acceptable[Int]
| implicit object LongOk extends Acceptable[Long]
| }
defined class Acceptable
defined module Acceptable
You could get a little mileage from the Either type. However the Either hierarchy is sealed and handling more than two types becomes cumbersome.
scala> implicit def string2either(s: String) = Left(s)
string2either: (s: String)Left[String,Nothing]
scala> implicit def int2either(i: Int) = Right(i)
int2either: (i: Int)Right[Nothing,Int]
scala> type SorI = Either[String, Int]
defined type alias SorI
scala> def foo(a: SorI) {a match {
| case Left(v) => println("Got a "+v)
| case Right(v) => println("Got a "+v)
| }
| }
foo: (a: SorI)Unit
scala> def bar(a: List[SorI]) {
| a foreach foo
| }
bar: (a: List[SorI])Unit
scala>
scala> foo("Hello")
Got a Hello
scala> foo(10)
Got a 10
scala> bar(List(99, "beer"))
Got a 99
Got a beer
Another solution is wrapper classes:
case class IntList(l:List[Int])
case class StringList(l:List[String])
implicit def li2il(l:List[Int]) = IntList(l)
implicit def ls2sl(l:List[String]) = StringList(l)
def foo(list:IntList) = { println("Int-List " + list.l)}
def foo(list:StringList) = { println("String-List " + list.l)}
There is this hack:
implicit val x: Int = 0
def foo(a: List[Int])(implicit ignore: Int) { }
implicit val y = ""
def foo(a: List[String])(implicit ignore: String) { }
foo(1::2::Nil)
foo("a"::"b"::Nil)
See http://michid.wordpress.com/2010/06/14/working-around-type-erasure-ambiguities-scala/
And also this question.