The question is why doesn't the following code work with type inference (below is a REPL session to demonstrate), and can it be fixed? More specifically, how is this different from the use of CanBuildFrom which the compiler uses to infer the return type?
Given this code:
object S {
import java.net._
trait UrlLike[T] {
def url(s: String): T
}
object UrlLike {
implicit object str extends UrlLike[String]{def url(s: String) = s}
implicit object url extends UrlLike[URL]{def url(s: String) = new URL(s)}
implicit object uri extends UrlLike[URI]{def url(s: String) = new URI(s)}
}
trait UrlSupport {
val _url: String
def url[T : UrlLike]: T = implicitly[UrlLike[T]].url(_url)
}
}
I have this session in the REPL (2.8.1):
scala> :load c:\temp\UrlTest.scala
Loading c:\temp\UrlTest.scala...
defined module S
scala> import java.net._
import java.net._
scala> import S._
import S._
scala> new UrlSupport{val _url = "http://example.com"}
res0: java.lang.Object with S.UrlSupport = $anon$1#155bd22
scala> res0.url : String
<console>:14: error: ambiguous implicit values:
both object uri in object UrlLike of type object S.UrlLike.uri
and object url in object UrlLike of type object S.UrlLike.url
match expected type S.UrlLike[T]
res0.url : String
^
scala> res0.url : URL
<console>:14: error: ambiguous implicit values:
both object uri in object UrlLike of type object S.UrlLike.uri
and object url in object UrlLike of type object S.UrlLike.url
match expected type S.UrlLike[T]
res0.url : URL
^
scala> res0.url[String]
res3: String = http://example.com
scala> res0.url[URL]
res4: java.net.URL = http://example.com
I can see why you'd expect it to work, but, obviously, the type inferencer isn't using the return type to infer T. I'd expect it too as well.
As for the ambiguity, CanBuildFrom avoids being ambiguous by just not defining everything in the same "level". For instance, this solves the ambiguity problem:
trait LowPriorityImplicits {
implicit object url extends UrlLike[URL]{def url(s: String) = new URL(s)}
implicit object uri extends UrlLike[URI]{def url(s: String) = new URI(s)}
}
object UrlLike extends LowPriorityImplicits {
implicit object str extends UrlLike[String]{def url(s: String) = s}
}
However, it will not make the type inference work the way you want:
scala> res0.url : URL
<console>:16: error: type mismatch;
found : String
required: java.net.URL
res0.url : URL
^
Which indicates it obviously makes T inference without taking into consideration the return type.
> trait UrlLike[T] {
trait UrlLike[+T] {
Regarding any implicit ambiguity, the rule is (since Scala2.8):
When comparing two different applicable alternatives of an overloaded method or of an implicit, each method:
gets one point for having more specific arguments,
and another point for being defined in a proper subclass.
An alternative “wins” over another if it gets a greater number of points in these two comparisons.
This means in particular that if alternatives have identical argument types, the one which is defined in a subclass wins.
I don't think the implicits around URL or URI get a different set of points following those criteria.
It might not be obvious from the error report that you're seeing, but all three of the implicits defined in object UrlLike are contributing to the ambiguity (eg. try commenting out the definition of uri and you'll see the ambiguity reported as being between str and url).
The reason for the ambiguity is that the type parameter T of UrlSupport.url is bounded only by the requirement that there be an implicit UrlLike instance available for it. String, URL and URI all satisfy that requirement equally well thanks the UrlLike instances provided by your three implicit objects. The compiler isn't going to choose one of those for you arbitrarily, so it reports an ambiguity.
Unless, of course, you resolve the ambiguity by explicitly specifying the type argument, as you've done in the last two of your REPL interactions.
Related
What I want to do is returning generics type in Scala function by using TypeTag. Here is the example code.
trait Parent[T]
object IntChild extends Parent[Int]
object StringChild extends Parent[String]
object SomeClass {
def of[A: TypeTag]: Parent[T] = {
getElementType[A] match {
case Int => IntChild
case String => StringChild
}
}
}
SomeClass.of[Array[Int]]
But it throws a compile error. Because the returned type of of method is not fixed in the compile type. Is there any way to get the type information from TypeTag and embed the type in the returned type?
What I am expecting is like
// T is inferred from TypeTag A.
def of[A: TypeTag, T]: Parent[T] = {
//...
}
I found this code also has not passed compile. So we need to fix the type information inferred from A's TypeTag.
def of[A: TypeTag]: Parent[_] = {
//...
}
This is the error.
type mismatch;
[error] found : Array[Int]
[error] required: Array[_$1]
How can I get the element type in advance?
I'm not sure it's possible with those definitions. How about changing the definitions a little?
trait Parent[T]
implicit object IntChild extends Parent[Int]
implicit object StringChild extends Parent[String]
object SomeClass {
def of[A: Parent]: Parent[A] = implicitly
}
This makes sure everything is done at the type level so that you get the return type you want. It requires the implicit modifier on IntChild and StringChild. There's no need to have another type parameter named T, as it would always be the same as A in your example.
I'm a bit confused about understanding of what type in scala means.
In documents I read that List[Int] is a type and List is a type constructor.
but what keyword type means when I write the following?
val ls: scala.collection.immutable.List.type = scala.collection.immutable.List
And how this type related to type that can be defined as a field in a trait for example.
In a scala val assignment like the above:
val name: Tpe = expression
Tpe is the type of the identifier name. Hence:
val x: List.type = List
The type of the List object (aka List module or List companion) is List.type. This indicates that it is a singleton. All of this is completely orthogonal to the concept of a type constructor in type theory.
Type Constructors
In type theory, List (note: not the companion) is both a type (denoted *) and a type constructor (denoted * -> *) because when you supply a type argument to List (e.g. Int) then you have a type (i.e. List[Int]). Scala provides syntax for this construction. For example:
def foo[F[_]]: F[Int]
^^^^ ^^^
| + supply a type argument to F[_]
|
+ the type constructor F[_]
But you cannot really implement such a method unless you know more about F. For example, if there is a implicit scalaz.Monad[F], you might use the Monad.pure value to construct your F[Int] like so:
def foo[F[_]: Monad]: F[Int] = Monad[F].pure(1)
In Scala
Scala does let you pass around List as a type constructor as a type parameter to such a method. For example:
scala> def foo[F[_]]: F[Int] = ???
foo: [F[_]]=> F[Int]
scala> lazy val x = foo[List]
x: List[Int] = <lazy>
However the List you are supplying in foo[List] is not the companion
Note the declaration of foo will result in the following warning:
<console>:11: warning: higher-kinded type should be enabled
by making the implicit value scala.language.higherKinds visible.
This can be achieved by adding the import clause 'import
scala.language.higherKinds'
or by setting the compiler option -language:higherKinds.
See the Scaladoc for value scala.language.higherKinds for a discussion
why the feature should be explicitly enabled.
def foo[F[_]]: F[Int] = ???
^
what keyword type means when I write the following
type is a member defined on object in Scala.
Suppose you have a class and its companion object:
class Foo(a: String)
object Foo {
def apply(a: String) = new Foo(a)
}
Now suppose you want to write a method that accepts the object Foo as an input. What's its type?
If you write this:
def someMethod(fooObj: Foo) = fooObj.apply("x") // doesn't compile
it's not going to compile. Foo refers to the type of an instance of the class (i.e. the one returned by new Foo("x") or Foo("x")). That's why objects have a type member that can refer to their own type:
def someMethod(fooObj: Foo.type) = fooObj.apply("x") // compiles!
In your specific example List.type is the type of the companion object of List. Here's a couple of examples that I hope will clarify what it means:
val listObj: List.type = List
val anEmptyList: List[Int] = listObj.empty[Int] // List()
val aListOfIntegers: List[Int] = listObj.range(1, 4) // List[(1, 2, 3)
And how this type related to type that can be defined as a field in a trait for example.
The type keyword defines a type member. .type is a type member. Conceptually it's like every object in Scala has a type member named type, like this:
object Foo {
type type = Foo
}
Obviously this won't compile, but it gives you an idea of what it may look like.
In type-level programming you’d think of a List[+A] being a type constructor:
That is:
-List[+A] takes a type parameter (A),
-by itself it’s not a valid type, you need to fill in the A somehow - "construct the type",
-by filling it in with String you’d get List[String] which is a concrete type.
In Scala it is not valid to say something is of type List. Scala is more strict here, and won’t allow us to use just a List in the place of a type, as it’s expecting a real type - not a type constructor.
In brief:
List[+A] is a type constructor whereas List[String] is a real type.
scala> case class Data[+T](val value:T=null)
defined class Data
scala> val foo=Data[ArrayBuffer[Data[Any]]]()
foo: Data[scala.collection.mutable.ArrayBuffer[Data[Any]]] = Data(null)
scala> foo.value+=Data[String]()
java.lang.NullPointerException
... 33 elided
I would like to have a Data class that is instantiated either as Data[String], Data[ArrayBuffer[Data[Any]]] or Data[Map[String,Data[Any]]]. In the above example I try to instantiate it as Data[ArrayBuffer[Data[Any]]] and add a Data[String] to its arraybuffer. Of course I get a null pointer exception because value is null. But the point of this example that it at least compiles and runs.
Now, in the Data constructor I would like to instantiate value as either a Data[String], an ArrayBuffer[Data[Any]] or Map[String,Data[Any]] depending on the type of the initially null value returned by the getClass method. However for this I need value to be a var, so that I can modify it after examining the type of its null value.
However I get this error:
scala> case class Data[+T](var value:T=null)
<console>:11: error: covariant type T occurs in contravariant position in type T of value value_=
case class Data[+T](var value:T=null)
Make your Data invariant in T. Just remove the +: Data[T] - this should compile.
Better yet, rethink your design to get rid of nulls and mutable variables - they both smell.
Edit: after reading your comments, I understand better what you are trying to do. Consider something like this for example as one of the options.
sealed trait Node
case class ListNode(list: Seq[Node]) extends Node
case class MapNode(map: Map[String, Node]) extends Node
case class LeafNode(data: String) extends Node
Now you can parse your document with something like (this is "pseudocode", adjust it to whatever xml-parsing library you are using):
def parseNode(tag: XMLTag): Node = tag.tagType match {
case LIST =>
val subNodes = tag.getNestedTags.map(parseNode)
ListNode(subNodes)
case MAP =>
val subNodes = tag.getNestedTags.map { tag =>
tag.name -> parseNode(tag)
}
MapNode(subNodes.toMap)
case _ =>
LeafNode(tag.text)
}
http://like-a-boss.net/2012/09/17/variance-in-scala.html#variance_and_type_safety
Variance and type safety
When defining a generic class with a var field we can get compile time errors:
scala> class Invariant[T](var t: T)
defined class Invariant
scala> class Covariant[+T](var t: T)
<console>:7: error: covariant type T occurs in contravariant position in type T of value t_=
class Covariant[+T](var t: T)
^
scala> class Contravariant[-T](var t: T)
<console>:7: error: contravariant type T occurs in covariant position in type => T of method t
class Contravariant[-T](var t: T)
Let’s break it down a little. Why doesn’t the compiler allow getters in the Covariant class?
scala> abstract trait Covariant[+T] {
| def take(t: T): Unit
| }
<console>:8: error: covariant type T occurs in contravariant position in type T of value t
def take(t: T): Unit
^
scala> abstract trait Contravariant[-T] {
| def take(t: T): Unit
| }
defined trait Contravariant
Why? Let’s think about usages of covariance let’s say that we have a class:
class Printer[+T] {
| def print(t: T): Unit = ???
| }
<console>:8: error: covariant type T occurs in contravariant position in type T of value t
def print(t: T): Unit = ???
If the print method can print Dogs does it make sense (in general) that it should also print Animals? Maybe sometimes but in the general sense if we want to generalize the Printer class we should use contravariance. The compiler is smart enough to check this type of usage for us.
Let’s think about the second use case: returning a generic parameter:
scala> class Create[-T] {
| def create: T = ???
| }
<console>:8: error: contravariant type T occurs in covariant position in type => T of method create
def create: T = ???
And again - does it make sense that Create should generalize by contravariance? If Create returns instances of the Animal class should we be able to use it in every place that expects Create[Dog]? The scala compiler is smart enough that it explodes in our face if we try it.
I can make it work this way:
package data
case class Data[+T](val value:T)
{
println(value.getClass)
}
This way I have to explicitly initialize value from the constructor. Nothing wrong with this, just I find it a bit too verbose.
import data.Data
import scala.collection.mutable.ArrayBuffer
import scala.collection.mutable.Map
object Run extends App
{
val a=Data[ArrayBuffer[Data[Any]]](ArrayBuffer[Data[Any]]())
a.value+=Data[String]("bar")
println(a.value)
val m=Data[Map[String,Data[Any]]](Map[String,Data[Any]]())
m.value+=("foo"->Data[String]("bar"))
println(m.value)
}
This prints:
class scala.collection.mutable.ArrayBuffer
class java.lang.String
ArrayBuffer(Data(bar))
class scala.collection.mutable.HashMap
class java.lang.String
Map(foo -> Data(bar))
The program only compiles with Data having +T type parameter, otherwise I get the error:
type mismatch;
[error] found : data.Data[String]
[error] required: data.Data[Any]
[error] Note: String <: Any, but class Data is invariant in type T.
[error] You may wish to define T as +T instead. (SLS 4.5)
[error] a.value+=Data[String]("bar")
I am trying to modify the Enterprise trait that extends the StarShip type.
(I apologize in advance for not knowing a proper title for this but I will update it after I understand the answer.)
For some reason I cannot get the parameters of the starShip that is passed.
I commented where I am getting the error.
Simple example to demonstrate this:
object Test {
trait Akira extends StarShip
trait Enterprise extends StarShip
sealed trait StarShip {
val captain: String
}
def doSomething[T: StarShip](starShip: T): T =
new T {
val captain = starShip.captain // Error: Cannot resolve symbol
}
doSomething(new Enterprise {
override val captain = "Picard"
})
}
Since I am passing an object of Enterprise I expect to get the same class out.
Edit:
Just realized that I want all variables in the starShip that is passed to be copied to the new class. With the exception that I will modify a few of them.
I believe the Monocle lib may solve my problem.
When I compile your code with the scala REPL, I get this:
<console>:17: error: Test.StarShip does not take type parameters
def doSomething[T: StarShip](starShip: T): T =
^
<console>:18: error: class type required but T found
new T {
^
<console>:19: error: value captain is not a member of type parameter T
val captain = starShip.captain // Error: Cannot resolve symbol
^
<console>:18: error: type mismatch;
found : T{}
required: T
new T {
^
As #dcastro mentioned, you probably wanted a type bound on T. However, even fixing that syntactical error isn't good enough because:
scala> def doSomething[T <: StarShip](starShip : T) : T = new T { val captain = starShip.captain }
<console>:8: error: class type required but T found
It is not possible to instantiate an object from a type parameter because the compiler does not know the actual type to instantiate until runtime. It could be anything, including a type written after the compilation of this function.
Based on your invocation of doSomething, I don't think you want the type parameter or the instantiation at all. That is, this works:
scala> def doSomething[T <: StarShip](starShip : T) : T = { val captain = starShip.captain; starShip }
doSomething: [T <: StarShip](starShip: T)T
scala> doSomething(new Enterprise { val captain = "Kirk" })
res0: Enterprise = $anon$1#26653222
So, I think with the above adjustments you have accomplished your goal. You've modified your trait (StarShip) by insantiating an Enterprise with an overriden value for the captain member.
I am struggling with a Scala implicit conversion problem. The following code snippet illustrates my problem :
import org.junit.{ Test, Before, After };
class ImplicitsTest {
implicit def toStringWrapper(str: String) = new StringWrapper(str);
#Test
def test(){
val res1: Predicate = "str" startsWith "other";
}
}
class StringWrapper(str: String){
def startsWith(other: String): Predicate = null;
}
trait Predicate
How can I force the String literal "str" to be converted through the implicit conversion toStringWrapper to get startsWith return Predicate instead of Boolean?
The code example doesn't compile. I am aware that String has already a startsWith method, I just want to use a different one, and I thought that using implicit conversions might be a way to do it.
Scala thankfully doesn't let you sneak replacement methods in without you noticing--if you call a method on a class, and the class has that method, that's the method call you get. To do otherwise would likely cause all sorts of confusion.
That leaves you with two other options:
(1) Rename the method
(2) Add a specific method to do the conversion.
The second approach works like so: you define a class that has both the method that you want and a uniquely-named method that returns itself, and optionally an implicit conversion from that class back to string if you want to be able to use the custom item like the original string (as if it had extended String):
object ImplicitExample {
class CustomString(s: String) {
def original = s
def custom = this
def startsWith(other: String): Int = if (s.startsWith(other)) 1 else 0
}
implicit def string_to_custom(s: String) = new CustomString(s)
implicit def custom_to_string(c: CustomString) = c.original
def test = {
println("This".custom.startsWith("Thi"))
println("This".custom.length())
}
}
scala> ImplicitExample.test
1
4
scala>
An implicit conversion is triggered in Scala only if the receiver does not contain the method being invoked or if an expression has a type different than the expected type.
Since the String object above contains the method startsWith, no implicit conversion is triggered. The compiler does, however, check if the type of the right hand expression "str".startsWith("other"); (that is Boolean) can be converted to a Predicate. Since there is no such implicit conversion in scope, it reports an error.
Note, also, that implicit conversions must be unambiguous. For instance, you might try to override Scala string behaviour for some other methods using implicit conversions. Java String objects are not Scala sequences (Seq), meaning that they do not have methods such as sorted, which returns the sorted version of the sequence. If you invoke it on a string:
scala> "string" sorted
res1: String = ginrst
This works because an implicit conversion defined in the Predef object gets triggered. Note that providing your own implicit conversion results in an error:
scala> implicit def wrap(s: String) = new { def sorted = "Hi!" }
wrap: (s: String)java.lang.Object{def sorted: java.lang.String}
scala> "string" sorted
<console>:7: error: type mismatch;
found : java.lang.String
required: ?{val sorted: ?}
Note that implicit conversions are not applicable because they are ambiguous:
...