I would like to add an implicit parameter to a class with a private constructor. Here as a simplified example:
class A[T] private(a:Int){
def this()=this(0)
}
If I would like to apply Pimp my library pattern to T with Ordered[T], I would need to use (the deprecated) view bound like so:
class A[T <% Ordered[T]] private(a:Int){
def this()=this(0)
}
And this works. However, to avoid the deprecated syntactic sugar I would like to pass the implicit parameter to the class. Unfortunately, this is where I'm probably doing something wrong:
class A[T] private(a:Int)(implicit conv:T=>Ordered[T]){
def this()=this(0)
}
For the above code, the compiler generates the following error:
error: No implicit view available from T => Ordered[T].
def this()=this(0)
While if I try to pass the implicit parameter directly like so:
class A[T] private(a:Int)(implicit conv:T=>Ordered[T]){
def this()=this(0)(conv)
}
I get this:
error: not found: value conv
def this()=this(0)(conv)
How does one pass an implicit parameter in this case?
EDIT: After some more experimentation it seems that redefining the constructor with implicit parameter is the problem. Not the fact that the constructor is private.
I found an answer, it seems that I need to explicitly define the implicit parameter for the parameterless constructor, e.g.:
class A[T] private(a:Int)(implicit conv:T=>Ordered[T]){
def this()(implicit conv:T=>Ordered[T])=this(0)
}
I apologize for spamming SO, in any case I will accept any answer that provides a more in depth explanation.
Scala provides ordering in 2 flavours, one via inheritance with Ordered and the other one which is actually a lot more appropriate in here is via context bounds using the Ordering typeclass.
Your approach is not actually idiomatic, and if you had something that actually used the implicit you provided, you would get an ambiguous implicit exception at compile time, because both the constructor and the method define the same implicit.
What I would do is:
class A[T : Ordering] private(a: Int)
This is actually shorthand syntax for:
class A[T] private(a: Int)(implicit ev: Ordering[T])
You can then use this argument either explicitly or implicitly.
If you define it with the shorthand T : Ordering syntax.
class A[T : Ordering] private(a: Int) {
def revSorter(list: List[T]): List[T] = {
list.sorted(implicitly[Ordering[T]].reverse)
}
}
If you define it with the "explicit" syntax:
class A[T] private(a: Int)(implicit ev: Ordering[T]) {
def revSorter(list: List[T]): List[T] = {
list.sorted(ev.reverse)
}
}
Related
I'm trying to get ClassTag implicitly. I have the following trait:
trait Tst{ self =>
type T
def getCls = getCls0[T] //no classtag
private def getCls0[T](implicit ev: ClassTag[T]) = ev.runtimeClass.asInstanceOf[Class[T]]
}
class Tsss extends Tst {
override type T = String
}
Is there a way to get ClassTag by the type variable declaration implicitly without specifying it explicitly?
The thing is I'm trying to make the code as easy to use for clients as possible.
I suggest you use abstract classes, implicit parameters and generics instead of parametric types:
abstract class Tst[T](implicit ev: ClassTag[T]) { self =>
def getCls = getCls0[T] //no classtag
private def getCls0[T](implicit ev: ClassTag[T]) =
ev.runtimeClass.asInstanceOf[Class[T]]
}
class Tsss extends Tst[String]
Note that although Dotty supports traits with parameters, it does not mix as well with generics.
The toArray call in following code does not compile
trait A[T] {
def create:T
def foo(a:Array[Int]) = {
for(b <- a) yield create
}.toArray
}
It throws the following errors:
not enough arguments for method toArray: (implicit evidence$1: scala.reflect.ClassTag[T])Array[T]. Unspecified value parameter evidence$1.
No ClassTag available for T
How do I fix it?
As Sergey said, Java arrays need to know the type of T, but T is eliminated by type erasure.
In scala you can "preserve" a type information at runtime using a ClassTag.
Here's a more in-depth discussion about arrays.
As per fixing it, you need to provide evidence of a ClassTag for T. Here's a possible solution:
import scala.reflect.ClassTag
trait A[T] {
def create: T
def foo(a: Array[Int])(implicit ev: ClassTag[T]) = {
for(b <- a) yield create
}.toArray
}
The implicit ev parameter is filled in automatically by the compiler.
The problem is that you can't create an Array[T] without knowing T. The ClassTag is Scala's way of representing this information. The simple fix would be to change trait A[T] to abstract class A[T: ClassTag] (class is needed because traits can't have any constructor parameters, including implicit ones). If you then create it with a specific type, e.g. class B extends A[Int], the compiler will insert the correct ClassTag itself, with a generic you need to pass the ClassTag through: class C[T: ClassTag] extends A[T].
The simplest way is to remove toArray call. Because you iterate over array so your result will be array too.
The setup for this example (Scala 2.10.3):
trait S[A]
trait T[A]
implicit class X[A : S](a: A) { def foo() { } }
implicit class Y[A : T](a: A) { def foo() { } }
implicit object I extends S[String]
This compiles:
new X("").foo()
This doesn't:
new Y("").foo()
because there is no implicit T[String].
could not find implicit value for evidence parameter of type T[String]
new Y("").foo()
^
Therefore, I would assume that scalac could unambiguously apply the implicit conversion from String to X:
"".foo()
But instead we get:
type mismatch;
found : String("")
required: ?{def foo: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method X of type [A](a: A)(implicit evidence$1: S[A])X[A]
and method Y of type [A](a: A)(implicit evidence$1: T[A])Y[A]
are possible conversion functions from String("") to ?{def foo: ?}
"".foo()
^
Is this intentional? Is scalac not supposed to consider whether each conversion would actually work when it enumerates the candidates?
My nonacademic view is that implicit is by design not meant to work everytime it seems that it should work. I think it's a good idea otherwise you could easily get into an implicit hell. You could extend your example by adding more layers of implicit conversions. It would be hard to tell which function is actually called just by looking at the code. There are well-defined rules, but I remember simply that if it's not obvious from the code what's going on, it doesn't work.
I would say that your code breaks One-at-a-time Rule which leads to breaking the Non-Ambiguity Rule. A : S is just a syntactic sugar, and can be rewritten to:
implicit class X[A](a: A)(implicit e: S[A]) { def foo() { } }
implicit class Y[A](a: A)(implicit e: T[A]) { def foo() { } }
Without resolution of the "second" implicit level (method argument e) both classes X and Y look the same to the compiler and therefore are ambiguous. As the linked document says: "For sanity's sake, the compiler does not insert further implicit conversions when it is already in the middle of trying another implicit."
It's kind of complicated to learn Scala's generic bounds. I know that:
T : Tr - T has type of Tr, meaning it implements a trait Tr
T <: SuperClass - T is subclass of SuperClass
T :> ChildClass - T is superclass of ChildClass
However, there are also many more operators:
<% and %>
=:=
<:< and >:>
<% and %>
<%< and >%>
I read about them, but as I said, there was was not comprehensible explanations. Could you make it clearer?
I've used a few type constraints:
The easiest are <: and >:. These are simple bounds on the type hierarchy.
trait A
trait B extends A
trait C extends B
then for a method
def doSomething[T >:C <:B](data:T)
either B or C can be substituted instead of T
Then type constraints that involves an addition of implicit parameter to the method.
def doSmth1[T: MyTypeInfo] (data:T)
is rewritten during compilation as
def doSmth1[T] (data:T)(implicit ev: MyTypeInfo[T])
whereas
def doSmth2[T <% SomeArbitratyType] (data:T)
is rewritten as
def doSmth2[T] (data:T)(implicit ev: T => SomeArbitratyType)
Both of the methods can be called if in the scope there is an instance that fits the implicit parameter. If there is no appropriate instance then compiler issues an error.
The view bound (<%) requires an implicit conversion that converts T to an instance of the other type (SomeArbitratyType).
More powerful is using "type classes". Inside the type class instance one may put many useful methods that can deal with the type T. In particular, one may put a conversion method and achieve similar result as view bounds.
Examples:
trait MyTypeInfo[T] {
def convertToString(data:T):String
}
def printlnAdv[T : MyTypeInfo](data:T) {
val ev = implicitly[MyTypeInfo[T]]
println(ev.convertToString(data))
}
somewhere in the scope there should be implicit value of type MyTypeInfo[T]:
implicit val doubleInfo = new MyTypeInfo[Double] {
def convertToString(data:Double):String = data.toString
}
or
implicit def convertToString(data:T):String
def printlnAdv[T <% String](data:T) {
val conversionResult = data : String
println(conversionResult)
}
somewhere in the scope there should be implicit function:
implicit def convertDoubleToString(data:Double):String = data.toString
The next weird symbols are =:= and <:<. These are used in methods that wish to ensure that a type has some property. Of course, if you declare a generic parameter then it is enough to have <: and >: to specify the type. However, what to do with types that are not generic parameters? For instance, the generic parameter of an enclosing class, or some type that is defined within another type. The symbols help here.
trait MyAlmostUniversalTrait[T] {
def mySpecialMethodJustForInts(data:T)(implicit ev:T =:= Int)
}
The trait can be used for any type T. But the method can be called only if the trait is instantiated for Int.
Similar use case exists for <:<. But here we have not "equals" constraint, but "less than" (like T<: T2).
trait MyAlmostUniversalTrait[T] {
def mySpecialMethod(data:T)(implicit ev:T <:< MyParentWithInterestingMethods)
}
Again the method can only can called for types that are descendants of MyParentWithInterestingMethods.
Then <%< is very similar to <%, however it is used the same way as <:< — as an implicit parameter when the type is not a generic parameter. It gives a conversion to T2:
trait MyAlmostUniversalTrait[T] {
def mySpecialMethod(data:T)(implicit ev:T <%< String) {
val s = data:String
...
}
}
IMHO <%< can safely be ignored. And one may simply declare the required conversion function:
trait MyAlmostUniversalTrait[T] {
def mySpecialMethod(data:T)(implicit ev:T => String) {
val s = data:String
...
}
}
I have this bit of code, which works:
val directions = rs.map(_.direction) // Direction extends Enumeration
directions == directions.sorted.reverse
I'd like to instead do something like this:
ratings.map(_.direction).isInBackwardsOrder
class RichSeq[T](seq: Seq[T]) {
def isInBackwardsOrder = seq == seq.sorted.reverse
}
object RichSeq {
implicit def seq2richSeq[T](seq: Seq[T]) = new RichSeq[T](seq)
}
I keep getting the following compilation error:
could not find implicit value for parameter ord: Ordering[T]
def isInBackwardsOrder = seq == seq.sorted.reverse
What I don't understand is why it could find the implicit value for parameter ord, when it was in the original form, but cannot find it once I pull it into a utility class.
Thanks for the help,
Alex
In the original form, you had no generics. directions is a Seq[SomeWellKnownType], and at compile time, the compiler looks for an Ordering[SomeWellKnownType] in implicit scope, and finds one.
On the other hand, in RichSeq[T], the compiler must find an implicit Ordering[T] where T is a type parameter. No way to do that. You must ensure that the Ordering will be available when you create the RichSeq :
class RichSeq[T](seq: Seq[T])(implicit ev: Ordering[T]) {...
There is a shortcut for that, especially if you just need ev in implicit scope without refrencing it explicitly, as in here :
class RichSeq[T : Ordering](seq: Seq[T]) {...
Then you have the exact same problem in your implicit method, which is generic too, with the same solution :
implicit def seq2richSeq[T: Ordering](seq: Seq[T]) = new RichSeq[T](seq)
Then it should work. The seq2richSeq implicit conversion will kick in when an Ordering is available for the type of the elements in the Seq.