Suppose, I have 2 typeclasses:
trait Foo[A] {
...
}
trait Bar[A] {
...
}
and then I collect their instances into collections:
val seqFoo : Set[Foo[_]] = ...
val seqBar : Set[Bar[_]] = ...
is there a way to combine this seqs into something like this:
val fooBar = (Foo[Int],Bar[Int]), ... , (Foo[String],Bar[String])
Maybe pattern matching function or type tags is an answer, but I believe this case should have more straightforward solution
For example you can make your type classes abstract classes rather than traits (then they can have parameters), add implicit TypeTag parameters, and make groupBy for your collections.
import scala.reflect.runtime.universe.{TypeTag, Type}
// type class
abstract class Foo[A](implicit val ttag: TypeTag[A]) {
def foo(): String // dummy method just to distinguish instances upon checking
}
// instances
object Foo1 {
implicit val intFoo: Foo[Int] = new Foo[Int]() {
override def foo(): String = "Foo1.intFoo"
}
implicit val strFoo: Foo[String] = new Foo[String]() {
override def foo(): String = "Foo1.strFoo"
}
}
object Foo2 {
implicit val intFoo: Foo[Int] = new Foo[Int]() {
override def foo(): String = "Foo2.intFoo"
}
implicit val boolFoo: Foo[Boolean] = new Foo[Boolean]() {
override def foo(): String = "Foo2.boolFoo"
}
}
object Foo3 {
implicit val intFoo: Foo[Int] = new Foo[Int]() {
override def foo(): String = "Foo3.intFoo"
}
implicit val strFoo: Foo[String] = new Foo[String]() {
override def foo(): String = "Foo3.strFoo"
}
implicit val boolFoo: Foo[Boolean] = new Foo[Boolean]() {
override def foo(): String = "Foo3.boolFoo"
}
}
val seqFoo : Set[Foo[_]] = Set(Foo1.intFoo, Foo1.strFoo, Foo2.intFoo, Foo2.boolFoo, Foo3.intFoo, Foo3.strFoo, Foo3.boolFoo)
val grouped: Map[Type, Set[Foo[_]]] = seqFoo.groupBy(_.ttag.tpe)
// checking that instances are grouped by type
grouped.view.mapValues(_.map(_.foo())).foreach(println)
//(Int,HashSet(Foo3.intFoo, Foo1.intFoo, Foo2.intFoo))
//(String,HashSet(Foo1.strFoo, Foo3.strFoo))
//(Boolean,HashSet(Foo2.boolFoo, Foo3.boolFoo))
Simlarly you can do groupBy for Bar and then do whatever you want with Map[Type, Set[Foo[_]]] and Map[Type, Set[Bar[_]]]. For example merge the Maps
val grouped: Map[Type, Set[Foo[_]]] = ???
val grouped1: Map[Type, Set[Bar[_]]] = ???
val merged: Map[Type, (Set[Foo[_]], Set[Bar[_]])] =
(grouped.keySet union grouped1.keySet)
.map(tpe => tpe -> (grouped(tpe), grouped1(tpe)))
.toMap
I'll copy from my comment. Instances of type classes are intended to be resolved automatically (via implicit resolution) by compiler at compile time. Collecting the instances manually seems weird, so does collecting them at runtime.
My typeclasses instances implement decoder/encoder logic. Also they contain some meta information (name particularly). So i would like to check name-to-type accordance and generate unique names, based on how many instances have same name but different type parameter.
Although names of implicits can sometimes make impact on implicit resolution (1 2 3 4), normally you shouldn't be interested much in names of implicits, nor in name-to-type correspondence. Maybe you should provide some code snippet so that we can understand your setting and goals. Maybe what you need can be done at compile time and/or automatically.
Alternatively, if you prefer to keep type classes traits or you can't modify their code, you can use magnet pattern (1 2 3 4 5 6 7 8)
trait Foo[A] {
def foo(): String
}
implicit class Magnet[F[_], A: TypeTag](val inst: F[A]) {
def tpe: Type = typeOf[A]
}
val seqFoo = Set[Magnet[Foo, _]](Foo1.intFoo, Foo1.strFoo, Foo2.intFoo, Foo2.boolFoo, Foo3.intFoo, Foo3.strFoo, Foo3.boolFoo)
val grouped: Map[Type, Set[Magnet[Foo, _]]] = seqFoo.groupBy(_.tpe)
// checking that instances are grouped by type
grouped.view.mapValues(_.map(_.inst.foo())).foreach(println)
//(String,HashSet(Foo1.strFoo, Foo3.strFoo))
//(Int,HashSet(Foo3.intFoo, Foo1.intFoo, Foo2.intFoo))
//(Boolean,HashSet(Foo2.boolFoo, Foo3.boolFoo))
Using the "Prolog in Scala" to find available type class instances
Related
I have class Seq and I want to have method "multy" that adds number to List factors, but I want, that only Seq of Number types will have this method, for this example, val numbers should work with multy, and val strings shouldn't.
import scala.collection.mutable.ListBuffer
object Main extends App{
val strings = new Seq("f", "d", "a")
val numbers = new Seq(1,5,4,2)
val strings2 = new Seq("c", "b")
numbers.multy(5)
strings.multy(5)
val strings3 = strings2.concat(strings)
println(strings3)
println(numbers)
}
class Seq[T : Ordering] (initialElems: T*) {
override def toString: String = elems.toString
val factors = ListBuffer[Number](1)
val elems = initialElems.sorted
def concat(a:Seq[T]) = new Seq(a.elems ++ this.elems:_*)
def multy[T <: Number](a:Number) = {
factors += a;
}
}
If you want scala.Int, scala.Long, scala.Double, etc to not be supported - because they don't extend Number - you can define method as
def multy(a: Number)(implicit ev: T <:< Number): Unit = {
factors += a
}
However, considering that under the hood they could be extending Number (if the compiler, decides that it should compile them as objects and not as primitives), usage of a type class would work better:
trait IsNumber[T] {
def asNumber(value: T): Number
}
object IsNumber {
implicit def numbersAreNumbers[T <: Number]: IsNumber[T] = t => t
implicit val intIsNumber: IsNumber[Int] = i => (i: Number)
... // the same for Long, Short, etc
}
which could be used in multy as evidence
def multy(a: Number)(implicit ev: IsNumber[T]): Unit = {
factors += a
}
and as normal type class in code that would need that knowledge for something:
def doSomethingWithElements(implicit isNumber: IsNumber[T]) = {
elems.map(isNumber.asNumber).toList // List[Number]
}
This would support both java.lang.* Numbers as well as Scala's numbers definitions.
Here is my problem:
trait Caller {
type EntityType
def parseEntity(entity: String): EntityType
}
trait IntCaller extends Caller {
implicit def strToInt(s: String) = s.toInt
override type EntityType = Int
override def parseEntity(entity: String): EntityType = entity
}
trait DoubleCaller extends Caller {
implicit def strToDouble(s: String) = s.toDouble
override type EntityType = Double
override def parseEntity(entity: String): EntityType = entity
}
object main {
def main(args: Array[String]): Unit = {
val intCaller = new IntCaller{}
val doubleCaller = new DoubleCaller{}
println("Result is: " + intCaller.parseEntity("5"))
println("Result is: " + doubleCaller.parseEntity("5.0"))
}
}
As you can see I keep repeating the code for: parseEntity method. If I wanted to add a FloatCaller I would have to rewrite parseEntity even though its implementation would be the same.
How can I write the implentation of the parseEntity in the Caller, so that I don't have to write the same code in the child traits again and again?
Disclaimer:
This is a simplification of a real problem I have with SprayJsonSupport from akka.http.scaladsl.marshallers.sprayjson.
You would be better off using a factory method that can build instances of a Caller given a conversion function. The only thing that's different between IntCaller and DoubleCaller is toInt and toDouble (and the types, of course).
trait Caller {
type EntityType
def parseEntity(entity: String): EntityType
}
object Caller {
def apply[A](f: String => A): Caller = new Caller {
type EntityType = A
def parseEntity(entity: String): EntityType = f(entity)
}
}
scala> val IntCaller = Caller(_.toInt)
scala> IntCaller.parseEntity("123")
res1: IntCaller.EntityType = 123
scala> val DoubleCaller = Caller(_.toDouble)
scala> DoubleCaller.parseEntity("1.23")
res2: DoubleCaller.EntityType = 1.23
If you want to keep using inheritance, then continue to force the sub-classes or traits to implement the conversion with parseEntity. It's not really necessary to using implicit conversions, though. The only reason it appears that there is repeated code is because the implicit conversion makes parseEntity look the same for each implementation, even though it's really not (because it needs to resolve a different implicit).
trait Caller {
type EntityType
def parseEntity(entity: String): EntityType
}
trait IntCaller {
type EntityType = Int
def parseEntity(entity: String): EntityType = entity.toInt
}
I have code that boils down to a Factory initializing an object and then using that object again to do additional operations:
trait Factory[T] {
def initialize(): T;
def finish(t: T): Unit;
}
As I understand this, the result of initialize should always be suitable to be passed to finish for any one Factory instance, regardless of T.
The factory itself is called at a point where it isn't known what T is:
object Minimal {
object StringFactory extends Factory[String] {}
val factories = Map[Int, Factory[_]](0 -> StringFactory)
val factory = factories(0)
// (1)
val obj = factory.initialize()
factory.finish(obj)
// (2)
def wrapper[T](factory: Factory[T]): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}
While variant (2) works, variant (1) doesn't:
type mismatch; found : Minimal.obj.type (with underlying type Any) required: _$6
but I can't figure out how to fix this. Is it even possible?
What does the compiler get by calling the wrapper method that it can't figure out itself? From my point of view, obj's type should be _$6, as the compiler seems to name that capture of _. How can I make the compiler realize that without having to introduce a whole new method for it?
Existential type looses its existentiality and becomes upper-bound after assigning its instance to val itself, so any way without such assigning will work, including:
scala> trait Factory[T] { type TT = T; def initialize(): TT; def finish(t: TT): Unit;}
defined trait Factory
scala> val factory: Factory[_] = new Factory[Int] {def initialize = 5; def finish(t: Int) {}}
factory: Factory[_] = $anon$1#31d0ca61
scala> factory.finish(factory.initialize())
This will not work:
scala> val obj = factory.initialize()
obj: Any = 5
scala> factory.finish(obj)
<console>:11: error: type mismatch;
found : Any
required: factory.TT
(which expands to) _$1
factory.finish(obj)
^
And this because scala will not see their types as equal (unless both are one same type member) as existentiality means that intialize() may return any subclass of Any, when finish() may accept any (but not always same) subclass of Any:
scala> trait Factory[T] { def initialize(): T; def finish(t: T): Unit;}
defined trait Factory
scala> val factory: Factory[_] = new Factory[Int] {def initialize = 5; def finish(t: Int) {}}
factory: Factory[_] = $anon$1#6e5da49
scala> factory.finish(factory.initialize())
<console>:10: error: type mismatch;
found : (some other)_$1(in value factory)
required: _$1(in value factory)
factory.finish(factory.initialize())
^
So the only way to bind input and output here is sharing type member between them.
One solution would be to entirely replace the type parameter with an abstract type:
trait Factory {
type T
def initialize(): T;
def finish(t: T): Unit;
}
object Minimal {
object StringFactory extends Factory {
type T = String
def initialize(): T = ???
def finish(t: T): Unit = ???
}
val factories = Map[Int, Factory](0 -> StringFactory)
val factory: Factory = factories(0)
// (1)
val obj: factory.T = factory.initialize()
// Or simply (relying on inference): val obj = factory.initialize()
factory.finish(obj)
// (2)
def wrapper(factory: Factory): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}
Based on Régis' answer, I found out that the compiler infers obj: Factory.T. From there, it was a small step to combine this with dk14's suggestion to use type TT = T. The result is this, buth generic and statically typechecked, without introducing a wrapper method. Kudos to both!
To literally answer the original question
From my point of view, obj's type should be _$6, as the compiler seems to name that capture of _. How can I make the compiler realize that without having to introduce a whole new method for it?
by giving _$6 the explicit name TT. Of course, the methods then need to use that name as well.
trait Factory[T] {
type TT = T
def initialize(): TT;
def finish(t: TT): Unit;
}
object Minimal {
object StringFactory extends Factory[String] {
def initialize(): TT = ""
def finish(t: TT): Unit = {}
}
val factories = Map[Int, Factory[_]](0 -> StringFactory)
val factory = factories(0)
// (1)
val obj: factory.TT = factory.initialize()
factory.finish(obj)
// (2)
def wrapper[T](factory: Factory[T]): Unit = {
val obj = factory.initialize()
factory.finish(obj)
}
wrapper(factory)
}
Is there a way in scala to call a method belonging to a type? For example, suppose I have a trait called Constructable that describes types than can construct a default instance of themselves. Then, I can write the following code:
trait Constructable[A] {
def construct: A
}
class Foo(x: Int) extends Constructable[Foo] {
def construct = new Foo(0)
}
def main(args: Array[String]) {
val f = new Foo(4)
println(f.construct)
}
This is ok, but what I really want is to be able to construct a default object given only the type of object. For example, suppose I want to accept a list of constructables and prepend a default object at the beginning of the list:
def prependDefault1[A <: Constructable[A]](c: List[A]): List[A] = {
val h = c.head
h.construct :: c
}
The above code works, but only if c is not empty. What I'd really like is to write something like the following:
def prependDefault2[A <: Constructable[A]](c: List[A]): List[A] = {
A.construct :: c
}
Is there any way to achieve this, possibly by changing the definition of a Constructable so that the construct method belongs to the "class" rather than the "instance" (to use Java terminology)?
You can't do this way, but you can do this using typeclasses:
trait Constructable[A] {
def construct: A
}
// 'case' just so it's printed nicely
case class Foo(x: Int)
// implicit vals have to be inside some object; omitting it here for clarity
implicit val fooConstructable = new Constructable[Foo] {
def construct = new Foo (0)
}
def prependDefault2[A : Constructable](c: List[A]): List[A] = {
implicitly[Constructable[A]].construct :: c
}
And then:
scala> prependDefault2(Nil: List[Foo])
res7: List[Foo] = List(Foo(0))
Some final remarks:
Implicits have to live inside an object. There are three places it can be located:
object Constructable { implicit val fooConstructable = ... (companion object of the typeclass trait)
object Foo { implicit val fooConstructable = ... (companion object of the class we implement typeclass for)
object SomethingElse { implicit val fooConstructable = ... (some random unrelated object)
Only in the last case you need to use import SomethingElse._ in order to be able to use the implicit.
After creating a ScalaIDE Worksheet named test.WsTemp, I wrote the code below and am receiving three errors for a single line in trait Enum:
Diverging implicit expansion for type scala.math.Ordering[U] starting with method ordered in trait LowPriorityOrderingImplicits
No implicit Ordering defined for U
Not enough arguments for method sorted: (implicit ord: scala.math.Ordering[U])List[U], Unspecified value parameter ord.
Why isn't this working since it's obvious Val extends Ordered[Val]?
object WsTemp {
trait Val extends Ordered[Val] {
val id: Int
val name: String
final def compare(that: Val) = this.id - that.id
override def toString: String = name
}
trait Enum[U <: Val] {
protected def init: Set[U]
val all = init
val allOrdered = all.toList.sorted // <-- Receiving error here
val byId = all.map(x => (x.id, x)).toMap
val byName = all.map(x => (x.name, x)).toMap
def apply(id: Int) = byId.get(id)
def apply(name: String) = byName.get(name)
}
sealed class Value(val id: Int, val name: String) extends Val
object Suit extends Enum[Value] {
override def init: Set[Value] = //Set()
Set(
new Value(0, "Spade")
, new Value(1, "Club")
, new Value(2, "Diamond")
, new Value(3, "Heart")
)
val Spade = Suit.byId(0)
val Club = Suit.byId(1)
val Diamond = Suit.byId(2)
val Heart = Suit.byId(3)
}
val all = Suit.all
val allOrdered = Suit.allOrdered
val byId = Suit.byId
val byName = Suit.byName
val spade = Suit.Spade
}
Ordering and Ordered are both invariant , so the fact that U is a sub-type of Val (as expressed by the relation U <: Val) does not imply that Ordering[Val] can be used as an Ordering[U].
So even though the fact that Val extends Ordered[Val] means that you implicitly get an Ordering[Val], this means nothing regarding Ordering[U].
So the compiler is right to complain that it cannot find an implicit value of type Ordering[U] (which is required by the call to sorted).
The prolbem is easy to illustrate with a small code snippet that mimics what happens with Ordered and Ordering:
object WsTemp {
trait MyOrdered[T]
trait MyOrdering[T]
object MyOrdering {
implicit def toOrdering[A <% MyOrdered[A]]: MyOrdering[A] = new MyOrdering[A]{}
}
trait Val extends MyOrdered[Val]
def test[T](implicit ord: MyOrdering[T]) {}
trait Enum[U <: Val] {
def callTest() { test[U] }
}
}
Which produces the following error:
<console>:20: error: could not find implicit value for parameter ord: WsTemp.MyOrdering[U]
def callTest() { test[U] }
But if you make MyOrdered and MyOrdering covariant, this compiles fine:
trait MyOrdered[+T]
trait MyOrdering[+T]
...
Obviously, you cannot change scala's Ordering nor Ordered to make them invariant.
Now, one way to solve your problem is to arrange your code so that Val does not extend Ordered[Val], but instead extends Ordered[X] where X is the actual type that you want to have an Ordering for (here, X = U). This can be achieved with F-bounded polymorphism:
trait Val[Self<:Val[Self]] extends Ordered[Self] {
//...
}
trait Enum[U <: Val[U]] {
//...
}
sealed class Value(val id: Int, val name: String) extends Val[Value]
//...
U is now a sub-type of Val[U], which is a sub-type of Ordered[U] (as opposed to a sub-type of Ordered[Val] as before), and so you now implicitly get an Ordering[U], which is (implicitly) passed to sorted. Problem solved.
As Régis Jean-Gilles said, "Ordering and Ordered are both invariant , so the fact that U is a sub-type of Val (as expressed by the relation U <: Val) does not imply that Ordering[Val] can be used as an Ordering[U]. So even though the fact that Val extends Ordered[Val] means that you implicitly get an Ordering[Val], this means nothing regarding Ordering[U]."
A very simple way to fix this is to specify the type to use for the sorted method. Replace:
val allOrdered = all.toList.sorted
with:
val allOrdered = all.toList.sorted[Val]