I am new to scala and am having an issue in calling a "generic" function in the java NIO library (from scala 2.10.x). Reducing the code to a simple test:
import java.net._
import java.nio.channels.{MembershipKey, DatagramChannel}
object Test {
val channel = DatagramChannel.open(StandardProtocolFamily.INET)
.setOption(StandardSocketOptions.SO_REUSEADDR, true)
...
}
This results in:
Error:(40, 48) type mismatch;
found : java.net.SocketOption[Boolean]
required: java.net.SocketOption[Any]
Note: Boolean <: Any, but Java-defined trait SocketOption is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
channel.setOption(StandardSocketOptions.SO_REUSEADDR, true)
^
I assume there is some way to resolve this without resorting to writing a wrapper in java. Have tried recasting in a variety of ways without success.
Question: how do I resolve the above?
Method setOption is polymorphic
setOption[T](name: SocketOption[T], value: T): DatagramChannel
so when calling
setOption(StandardSocketOptions.SO_REUSEADDR, true)
scala see that types of arguments are slightly different
StandardSocketOptions.SO_REUSEADDR: SocketOption[java.lang.Boolean]
true: scala.Boolean
and compiler tries to narrow T to the most common type between java.lang.Boolean <: AnyRef and scala.Boolean <: AnyVal which is Any
to fix this issue, you need to either provide explicit type for setOption
setOption[java.lang.Boolean](StandardSocketOptions.SO_REUSEADDR, true)
or use type ascription (then T will be inferred correctly)
setOption(StandardSocketOptions.SO_REUSEADDR, true: java.lang.Boolean)
Related
I am trying to create a method which could take a type parameter of any descendant of immutable.Seq. This is what I got so far:
def writeSomeData[Holder[_] <: Seq[String]](path: String, holder: Holder[String]): Unit = {
// irrelevant implementation
}
However when I call the above method with an immutable.List:
writeSomeData(tmp, List(res1, res2, res3, res4, res5))
it breaks with the following error:
[error] /home/v.gorcinschi/repos/...: inferred type arguments [List] do not conform to method writeSomeData's type parameter bounds [Holder[_] <: Seq[String]]
[error] writeSomeData(tmp, List(res1, res2, res3, res4, res5))
[error] ^
[error] /home/v.gorcinschi/repos/...: type mismatch;
[error] found : List[String]
[error] required: Holder[String]
[error] writeSomeData(tmp, List(res1, res2, res3, res4, res5))
Why is this happening and how must I correct it? List is a descendant of a Seq isn't it?
The problem is that Holder[_] <: Seq[String] doesn't mean what you think it does. I am actually not sure what does it mean, probably something like: trait Holder[A] extends Seq[String] which as you can see is very different from how List would look like.
You actually want to express that Holder[String] should be a subtype of Seq[String] many people think the right syntax for that is Holder[_] <: Seq[_] However, that is also incorrect; that just means that Holder must extend Seq but doesn't guarantee that Holder[String] <: Seq[String]
Thankfully, Scala does provides syntax to represent that: Holder[x] <: Seq[x] that x is not another type parameter, is just a way to say that Holder[x] is a subtype of Seq[x] for any type x
Sometimes, you it is also easier to use a generalized type constraint like:
def foo[Holder[_]](holder: Holder[String])(implicit ev: Holder[String] <:< Seq[String]): Unit
To represent that exact same relationship.
Nevertheless, that is only useful if you reference Holder in the return type, if you only want to consume any Seq then you can just do:
def foo(holder: Seq[String]): Unit
Thanks to Liskov, you can pass any subtype of Seq[String] there.
The type constraint
Holder[_] <: Seq[String]
means method takes any type constructor Holder such that Holder[X] is a subtype of Seq[String] for arbitrary X. By arbitrary we mean X >: Nothing <: Any.
Now when you pass List("") as argument then Scala will try to unify
?Holder := List
such that Holder[X] <: Seq[String] for arbitrary X. However List[X] is not a subtype of Seq[String] for arbitrary X, for example take X := Int. Hence it does not type check.
As a workaround we can do
Holder[x <: String] <: Seq[x]
This effectively achieves the same thing but this time it works because now
Holder[x] <: Seq[x]
does hold true for arbitrary x because both List and Seq are similarly defined
trait Seq[+A]
trait List[+A]
and it is true that [X] =>> List[X] <: [X] =>> Seq[X] for arbitrary x.
Plus compiler has extra information x <: String from Holder[x <: String] to check the element type of the argument.
Given
Holder[_] <: Seq[String]
You could make it work by helping out the compiler with explicitly passing appropriate type lambda something like so
writeSomeData[[X] =>> List[String]](???, List(""))
but Scala does not infer such type lambdas automatically I think because of in general type constructor unification being undecidable.
I am starting to embrace abstract type members over type parameters - mainly because they seem to work better with type inference for me. However, I am still struggling to understand how to use them from outside of the types they are defined in. For example, I cannot understand why the following Scala program should not compile:
trait Thing
trait Describer {
type X<:Thing
def describe(x:X) = println(x)
}
object Program extends App {
def print[T <: Thing, D <: Describer]
(describer: D, thing:T)
(implicit ev: D#X =:= T)
= describer.describe(thing)
}
Intuitively, I would expect that the requirement D#X =:= T would guarantee that the two types are indeed equal and hence that instances of two could be used interchangeably, but I get this compilation error:
error: type mismatch;
found : thing.type (with underlying type T)
required: describer.X
= describer.describe(thing)
What have I misunderstood? Is there another way to do what I want to do? Or failing that, is it safe to cast thing to the required type (describer.X)?
The type of the parameter of describer.describe is actually describer.X and not D#X. Another thing you have to know is that A =:= B also functions as a conversion from A to B but (at least currently) not the other way around. So the following should work.
def print[T <: Thing]
(describer: Describer, thing: T)
(implicit ev: T =:= describer.X)
= describer.describe(thing)
This seems to be a classic question for developers used to Scala type-level programming, but I couldn't find (or I don't know how to search for) a solution or pattern for this. Suppose I have a class like this:
abstract class TypedTest[Args <: HList](implicit val optMapper: Mapped[Args, Option]) {
type OptArgs = optMapper.Out
def options: OptArgs // to be implemented by subclasses
}
I want users of this class to instantiate it with an HList type parameter (Args) and the class provides a method to retrieve an HList instance containing an instance of each specified type inside an Option (OptArgs). I'm using shapeless Mapped type class for this. Note that I don't have an instance of Args to provide at instantiation time.
This code doesn't work, as the compiler doesn't infer the concrete type of OptArgs and even an obviously correct implementation such as def options = HNil yields a compilation error. The same code using the Aux pattern:
abstract class TypedTest[Args <: HList, OptArgs <: HList](implicit val optMapper: Mapped.Aux[Args, Option, OptArgs]) {
def options: OptArgs
}
This forces me to specify both lists at instantiation time, which makes the external API needlessly verbose. Is there an workaround for this?
This is my understanding, but I'm not 100% sure and will be happy to stand corrected.
The type member TypedTest.OptArgs is not an abstract type but a type alias. It is the same type for all subclasses of TypedTest – an alias for Mapped[Args, Option].Out, which is an abstract type and cannot be unified with any type but itself. When a subclass is created, the type member OptArgs is not overridden.
It becomes more clear when using Mapped.Aux with an existential type for Out0, which is IIUC more or less equivalent to the above:
abstract class TypedTest[Args <: HList](
implicit val optMapper: Mapped.Aux[Args, Option, T] forSome { type T }) {
type OptArgs = optMapper.Out
def options: OptArgs // to be implemented by subclasses
}
val intTest = new TypedTest[Int :: HNil] {
def options = Some(1) :: HNil
}
Error:(18, 29) type mismatch;
found : shapeless.::[Some[Int],shapeless.HNil]
required: this.OptArgs
(which expands to) T
def options = Some(1) :: HNil
Unfortunately I'm not aware of any possible solution, except adding Out as a type parameter or defining OptArgs as an abstract type and specifying it explicitly in each subclass.
When trying to use higher kinded existentials in Scala I run into the following problem:
trait A[H[_]]
trait Test {
val l: A[List]
// [error] type mismatch;
// [error] found : A[List]
// [error] required: A[_[_] <: Any]
// [error] Note: List <: Any, but trait A is invariant in type H.
// [error] You may wish to define H as +H instead. (SLS 4.5)
val x: A[B] forSome { type B[_] } = l
}
Adding a covariant annotation to H as the compiler suggests works. Is there a way to work around this if I don't want H to be covariant?
A slight variation of the example gives a more helpful error message:
scala> l: (A[B] forSome { type B[_] })
<console>:10: error: type mismatch;
found : A[List]
required: A[_[_] <: Any]
Note: List <: Any, but trait A is invariant in type H.
You may wish to define H as +H instead. (SLS 4.5)
l: (A[B] forSome { type B[_] })
^
<console>:10: error: can't existentially abstract over parameterized type B
l: (A[B] forSome { type B[_] })
^
Looking for this error brings us to a TODO in the compiler.
Since existential types are going to disappear, per Odersky's email, I don't think this limitation will be fixed. However, Martin Odersky's email also reminds us that existential types are equivalent to abstract types. Hence, the above example can be encoded as follows:
scala> trait A { type H[_] }
defined trait A
scala> val l: A {type H[X] = List[X]} = null
l: A{type H[X] = List[X]} = null
scala> l: A
res0: A = null
Type application is syntactically ugly, but turning a value into an existential becomes trivial (also to implement in a compiler, which is part of Odersky's point).
What's useful to encode existentials is that type members don't have to be instantiated ever. So, to encode A[_] we can write:
scala> class A { type T }
defined class A
scala> new A
res1: A = A#3a7049a6
What's confusing here is that this does not work for objects:
scala> object B { type T }
<console>:8: error: only classes can have declared but undefined members
object B { type T }
^
But I recently got that accepted as a bug — see here for a pull request clarifying the spec (approved by Adriaan Moors), and here for my bug report and one-line fix to the compiler (still waiting for review).
I'm trying to call this set method documented here, in the Java library jOOQ, with signature:
<T> ... set(Field<T> field, T value)
This Scala line is a problem:
.set(table.MODIFIED_BY, userId)
MODIFIED_BY is a Field<Integer> representing the table column. userId is Int. Predef has an implicit conversion from Int to Integer, so why doesn't it use it? I get this:
type mismatch; found: org.jooq.TableField[gen.tables.records.DocRecord,Integer]
required: org.jooq.Field[Any]
Note: Integer <: Any
(and org.jooq.TableField[gen.tables.records.DocRecord,Integer] <:
org.jooq.Field[Integer]), but Java-defined trait Field is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
Update - About Vinicius's Example
Rather than try to explain this in comments, here is a demonstration that there is no implicit conversion being called when you use a type with covariant parameter, like List[+T]. Let's say I put this code in a file, compile, and run it...
case class Foo(str: String)
object StackOver1 extends App {
implicit def str2Foo(s: String): Foo = {
println("In str2Foo.")
new Foo(s)
}
def test[T](xs: List[T], x: T): List[T] = {
println("test " + x.getClass)
xs
}
val foo1 = new Foo("foo1")
test(List(foo1), "abc")
}
You'll see that it calls test, but never the implicit conversion from String "abc" to Foo. Instead it's picking a T for test[T] that is a common base class between String and Foo. When you use Int and Integer it picks Any, but it's confusing because the runtime representation of the Int in the list is Integer. So it looks like it used the implicit conversion, but it didn't. You can verify by opening a Scala prompt...
scala> :type StackOver1.test(List(new java.lang.Integer(1)), 2)
List[Any]
I don't know anything aboutjOOQ, but I think the issue is that Scala does not understand java generics very well. Try:
scala> def test[T](a : java.util.ArrayList[T], b: T) = { println(a,b) }
scala> val a = new java.util.ArrayList[Integer]()
scala> val b = 12
scala> test(a,b)
<console>:11: error: type mismatch;
found : java.util.ArrayList[Integer]
required: java.util.ArrayList[Any]
Note: Integer <: Any, but Java-defined class ArrayList is invariant in type E.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
test(a,b)
Sounds familiar??
And to fix, just inform the type T to call the method: test[Integer](a,b) works fine.
EDIT:
There a few things involved here:
Erasure -> When compiled the type of the generic will disappear by erasure. The compiler will use Object which Scala, will treat as Any. However a ArrayList[Integer] is not an ArrayList[Any], even though Integer is any. The same way that TableField[gen.tables.records.DocRecord,Integer] is not a Field[Any].
Type inference mechanism -> it will figure out what type T should be and to do that it will use the intersection dominator of the types passed (in our case the first common ancestor). Page 36 of Scala Language Spec, which in our examples above will lead use to Any.
Implicit conversion -> it is the last step and would be called if there was some type to be converted to another one, but since the type of the arguments were determined to be the first common ancestor, there is no need to convert and we will never have a implicit conversion if we don't force the type T.
A example to show how the common ancestor is used to determine T:
scala> def test[T](a: T, b: T): T = a
scala> class Foo
scala> class Boo extends Foo
scala> test(new Boo,new Foo)
res2: Foo = Boo#139c2a6
scala> test(new Boo,new Boo)
res3: Boo = Boo#141c803
scala> class Coo extends Foo
scala> test(new Boo,new Coo)
res4: Foo = Boo#aafc83
scala> test(new Boo,"qsasad")
res5: Object = Boo#16989d8
Summing up, the implicit method does not get called, because the type inference mechanism, determines the types before getting the argument and since it uses the common ancestor, there is no need for a implicit conversion.
Your code produces an error due to erasure mechanism which disappear with the type information that would be important to determine the correct type of the argument.
#RobN, thanks for questioning my answer, I learned a lot with the process.