Scala: using type parameters or abstract types as type bounds - scala

Suppose I have:
class Bounded[A] {
type apply[C <: A] = C
}
This compiles:
implicitly[Bounded[Any]#apply[String] =:= String]
This fails:
type Str = Bounded[Any]#apply[String]
...with:
[error] /home/grant/Workspace/scunits/test/src/main/scala/Box.scala:37: type arguments[String] do not conform to type apply's type parameter bounds [C <: A]
[error] type Str = Bounded[Any]#apply[String]
[error] ^
I tried using abstract types instead of type parameters, with the same result. The only work-around I found was to instantiate the type. This compiles:
val boundedAny = new Bounded[Any]
type Str2 = boundedAny.apply[String]
Unfortunately I'm working with phantom types which don't have run time instances, often for performance reasons.
Why does Scala produce a compile error here? Is there a better work-around?
Thanks for any help.
Update: In addition to the workaround below, I needed a way to override types with abstract type bounds. I did this like so:
object Test {
class AbstractBounded[A] {
type apply[C <: A] <: A
class Workaround[C <: A] {
type go = apply[C]
}
}
class Bounded[A] extends AbstractBounded[A] {
type apply[C <: A] = C
}
type Str = Bounded[Any]#Workaround[String]#go
}

How about:
scala> class Bounded[A] { class i[C <: A]{ type apply = C}}
defined class Bounded
scala> type TTT = Bounded[Any]#i[String]#apply
defined type alias TTT
scala> implicitly[TTT =:= String]
res4: =:=[TTT,String] = <function1>
Scala forgot to lookup generic (or another "abstract" type) before binding parameter to type alias. Given that =:= works fine - seems like a bug for me. Maybe implicits are resolving on another compilation level or just before this check.

Related

Implicit class resolution for parameterized types

In the following example, it seems that the Scala compiler only recognizes an implicit class when it is defined to take the higher-kinded representation of Wrapper. Why is that?
scala> case class Nested(n: Int)
defined class Nested
scala> case class Wrapper[A <: Product](nested: A)
defined class Wrapper
scala> implicit class I1[W <: Wrapper[A], A <: Product](underlying: W) {
| def ok1() = true
| }
defined class I1
scala> Wrapper(Nested(5)).ok1()
<console>:26: error: value ok1 is not a member of Wrapper[Nested]
Wrapper(Nested(5)).ok1()
^
scala> implicit class I2[W <: Wrapper[_]](underlying: W) {
| def ok2() = true
| }
defined class I2
scala> Wrapper(Nested(5)).ok2()
res1: Boolean = true
Is there a workaround for implicit resolution that maintains full information about the nested type, allowing typeclass evidence, e.g., TypeTag, to be attached to it?
Note: the example above shows Nested and Wrapper to be case classes but that's not integral to the question. It's simply a convenience for a shorter and simpler console session.
This is happening because of a limitation in Scala's type inference. See SI-2272.
The implicit fails to resolve because the compiler cannot properly infer A. We can see this if we enable -Xlog-implicits. Notice that A is inferred as Nothing:
I1 is not a valid implicit value for Test.w.type => ?{def ok: ?} because:
inferred type arguments [Wrapper[Nested],Nothing] do not conform to method I1's type parameter bounds [W <: Wrapper[A],A <: Product]
The same thing happens if we try to instantiate I1 manually:
scala> val w = Wrapper(Nested(5))
w: Wrapper[Nested] = Wrapper(Nested(5))
scala> new I1(w)
<console>:21: error: inferred type arguments [Wrapper[Nested],Nothing] do not conform to class I1's type parameter bounds [W <: Wrapper[A],A <: Product]
new I1(w)
^
<console>:21: error: type mismatch;
found : Wrapper[Nested]
required: W
new I1(w)
^
Now, the work-arounds.
First, Wrapper is a case class, so there shouldn't be a reason for it to have sub-types. You can remove the W type parameter, and change underlying to a Wrapper[A]:
implicit class I1[A <: Product](underlying: Wrapper[A]) {
def ok = true
}
If you still wish to require two type parameters, you can also require implicit evidence that W <:< Wrapper[A], while removing the upper-bound on the type parameter W:
implicit class I1[W, A <: Product](underlying: W)(implicit ev: W <:< Wrapper[A]) {
def ok = true
}
Everything Michael said is true. Here is some extra perspective on this issue.
Because of the way you wrote your implicit class it looks like you want the implicit class to work on all subtypes of Wrapper and have as specific information about all types involved as possible. (99% of the time it's a bad idea to extend case classes, but it is possible, and these tricks also work for non case classes).
The trick basically is to make sure that all the type parameters that you want inferred are present somewhere in the value parameter lists. Another thing to keep in mind is this:
scala> trait Foo[A]; trait Bar extends Foo[Int]
defined trait Foo
defined trait Bar
scala> implicitly[Bar with Foo[Int] =:= Bar]
res0: =:=[Bar with Foo[Int],Bar] = <function1>
Take these two pieces of knowledge and you can rewrite your implicit class like this:
implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
def ok1(): (Y, A) = ???
}
And see it at work:
scala> :paste
// Entering paste mode (ctrl-D to finish)
case class Nested(n: Int)
case class Wrapper[A <: Product](nested: A)
class Crazy(override val nested: Nested) extends Wrapper[Nested](nested)
implicit class I1[Y, A <: Product](underlying: Y with Wrapper[A]) {
def ok1(): (Y, A) = ???
}
// Exiting paste mode, now interpreting.
scala> :type Wrapper(Nested(5)).ok1()
(Wrapper[Nested], Nested)
scala> :type new Crazy(Nested(5)).ok1()
(Crazy, Nested)
Note that the last solution Michael gave is based on the same thing: by moving the upper bound to the implicit parameter list, A is now present in the value parameter lists and can be inferred by the compiler.

aliasing proper type as existential type (why it compiles?)

Why can I do the following:
class A
type M[_] = A
I would expect I can only alias type that expects one type parameter, for example a List[_], but it works also with plain classes.
If I create a method:
def foo(m: M[_]) = m
and call it with wrong parameter:
scala> foo("a")
<console>:15: error: type mismatch;
found : String("a")
required: M[_]
(which expands to) A[]
foo("a")
I get such error. What is the meaning of A[]?
Going further, how to explain this:
scala> type M[_, _] = A
<console>:12: error: _ is already defined as type _
type M[_, _] = A
Is there a way to assure that what I put on the right hand side of my alias will be a parametrized type?
type M[_] = A is the same as type M[X] = A: a constant function on types. M[X] is A whatever X is: M[Int] is A, M[String] is A, M[Any] is A, etc. _ in this case is just an identifier (which explains the error for type M[_, _] as well).
Of course, in def foo(m: M[_]) = m, M[_] is an existential type: M[T] forSome { type T }. I don't know why Scala says it expands to A[] in the error message, though; this may be a bug. You can check it's the same type as A by calling
scala> implicitly[M[_] =:= A]
res0: =:=[A[],A] = <function1>
Is there a way to assure that what I put on the right hand side of my alias will be a parametrized type?
You can declare an abstract member type with higher-kind
trait Foo { type M[_] }
and it can only be implemented by parametrized types:
class Bar1 extends Foo { type M = Int } // fails
class Bar2 extends Foo { type M[X] = List[X] } // works
Of course, as mentioned in the first paragraph, M in type M[X] = Int is parametrized, and I don't think there is a way to rule it out.

Scala type members variance

Consider this short snippet:
trait Table[+A] {
type RowType = Seq[A]
}
Scala 2.11.7 compiler gives the following error:
covariant type A occurs in invariant position in type Seq[A] of type RowType
Why is A considered to be in invariant position in Seq[A] while Seq itself is defined as trait Seq[+A]?
Also, could you please provide a use case demonstrating possible issues with this type definition if we ignore the error?
For any B <: A your Table[B]#RowType will be more concrete than Table[A]#RowType. More concrete does not mean the same, so compiler is considering parameters for type aliases as invariant positions.
How you could fix this.
Abstract member
You can define your type as abstract, that mean you should define it later and probably stuck with same problem, but on trait Table level such definition will be correct
trait Table[+A] {
type RowType <: Seq[A]
}
Concrete higher-kinded type
You can define parameterized type member, which could lead to changing how you can use this type, but in most cases should do the job.
trait Table[+A] {
type RowType[+X] = Seq[X]
}
On type member variance
Not the strongest my field but i try to describe my thoughts.
Suppose you have
trait Table[+A] {
type RowType = Seq[A]
}
def mkTable[A]: Table[A] = new Table[A] {}
then you do following
val tupleTable = mkTable[(String, String)]
val prodTable: Table[Product] = tupleTable
So what would be prodTable.RowType?
In case of your definition it should be Seq[Product]. But wait, prodTable and tupleTable are the same object, so their members should be the same, so prodTable.RowType should be Seq[(String, String)]
But if you change to the first approach like
trait Table[+A] {
type RowType <: Seq[A]
}
def mkTable[A]: Table[A] = new Table[A] {
type RowType = Seq[A]
}
Compiler would know that RowType for Table[Product] is some type <: Seq[Product] which is true for Seq[(String, String)] and all ambiguilties are gone.
Here's a counter-example:
import scala.annotation.unchecked.uncheckedVariance
class Foo[+S](x: S) {
type Shape = S #uncheckedVariance // THIS IS BAD
}
object Test {
def main(args: Array[String]): Unit = {
val fs: Foo[String] = new Foo("")
def cast(x: Foo[Any]): x.Shape = 1 // This typechecks since x.Shape =:= Any
val s: String = cast(fs) // This typechecks because Foo[String] <: Foo[Any], but results in a ClassCastException
}
}
which throws:
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Test$.main(main.scala:12)
Scastie link: https://scastie.scala-lang.org/smarter/Cno5sLZyTSybDC80uDt2hQ. Credit to #smarter.

Understanding “inferred type arguments do not conform to type parameter bounds” errors in Scala

I fail to understand why I am getting an “inferred type arguments do not conform to type parameter bounds”.
First, I defined a trait called CS which may be implemented by several classes (e.g., CS01 and CS02):
trait CS[+T <: CS[T]] {
this: T =>
def add: T
def remove: T
}
class CS01 extends CS[CS01] {
def add: CS01 = new CS01
def remove: CS01 = new CS01
}
class CS02 extends CS[CS02] {
def add: CS02 = new CS02
def remove: CS02 = new CS02
}
The idea is to keep the implemented type when calling add or remove on CS01 and CS02.
Secondly, I would like to define operations that may be executed on every classes compliant with trait CS. Then, I defined a trait called Exec(with two very simple examples of classes Exec01 and Exec02 mixin the Exec traits):
trait Exec {
def exec[U <: CS[U]](x: U): U
}
class Exec01 extends Exec {
def exec[U <: CS[U]](x: U): U = x.add
}
class Exec02 extends Exec {
def exec[U <: CS[U]](x: U): U = x.remove
}
Once again, I need to keep the implemented type of the class that mixes the CS trait. That is why exec is parametrized with [U <: CS[U]].
Finally, I want any CS enabling operations on it to mixin the trait Executable which makes it possible to execute an operation that follows trait Exec:
trait Executable[T <: CS[T]] {
this: T =>
def execute(e: Exec): T = e.exec(this)
}
However, I get the following error when I am trying to compile:
error: inferred type arguments [this.Executable[T] with T] do not conform to method exec's type parameter bounds [U <: this.CS[U]]
def execute(e: Exec): T = e.exec(this)
^
I don't quite understand because any classes that mix Executable must be of type T with the constraint of mixin the CS trait due to the bound in trait Executable[T <: CS[T]]. So, why this does not conform to the type parameter bound U <: CS[U] ?
Works if you specify the type parameter to exec explicitly:
def execute(e: Exec): T = e.exec[T](this)
Seems to be a limitation in the type inference.
Disclaimer: not a scala guru here, I'm learning it as I'm writing this.
First, let's simplify the example.
scala> trait Moo[+X <: Moo[X]]
defined trait Moo
scala> class Foo extends Moo[Foo]
defined class Foo
scala> def foobar[U <: Moo[U]](x: U) = x
foobar: [U <: Moo[U]](x: U)U
scala> foobar(new Foo)
res0: Foo = Foo#191275b
scala> class Bar extends Foo
defined class Bar
scala> foobar(new Bar)
<console>:12: error: inferred type arguments [Bar] do not conform to method
foobar's type parameter bounds [U <: Moo[U]]
foobar(new Bar)
^
scala>
foobar accepts a Foo argument but rejects a Bar which only extends Foo. Why? foobar is a generic, paramaterized by the type of its argument. It imposes a bound on that type. The type inferencer will not check each and every ancestor of the argument type, hoping to find one that satisfies the bound.
So how to impose a bound on an ancestor type? One method is with existential types.
scala> def foobar[V <: Moo[U] forSome {type U}](x: V) = x
foobar: [U <: Moo[_], V <: U](x: V)V
scala> foobar(new Foo)
res3: Foo = Foo#1154718
scala> foobar(new Bar)
res4: Bar = Bar#5a7ff7
scala>

More Scala typing issues

Now that Kim Stebel helped me understanding how to type variables with existential types, i need to know how to use them in inheritance:
the following code doesn’t compile:
class PagingListModel(startPageNumber: Int, pageSize: Int, query: Query[Projection[_ <: Product]]) extends AbstractListModel {
val itemStartNumber: Int = startPageNumber * pageSize
val items: List[Product] = getPageData()
override def getPageData(): List[Product] = {
db withSession {
return queryToQueryInvoker(query.drop(itemStartNumber).take(pageSize)).list
}
}
}
…with the error:
no type parameters for method queryToQueryInvoker:
(q: org.scalaquery.ql.Query[org.scalaquery.ql.ColumnBase[T]])
org.scalaquery.ql.basic.BasicQueryInvoker[T]
exist so that it can be applied to arguments
(org.scalaquery.ql.Query[org.scalaquery.ql.Projection[_ <: Product]])
--- because ---
argument expression's type is not compatible with formal parameter type;
found : org.scalaquery.ql.Query[org.scalaquery.ql.Projection[_ <: Product]]
required: org.scalaquery.ql.Query[org.scalaquery.ql.ColumnBase[?T]]
…which is strange, because the required type is really inside the bounds of the found one…
PS: i really only want to be able to call foreach on each Tuple in the list returned by getPageData()…
I don't think this can be done with an existential type. It works with a type parameter:
class PagingListModel[T <: Product](... query: Query[Projection[T]]) {
...
def getPageData(): List[_ <: Product] = ...
queryToQueryInvoker(query.drop(itemStartNumber).take(pageSize)).list
}
The original version would be correct but Scala cannot typecheck it due to a limitation similar to Haskell's monomorphism restriction. The type parameter for queryToQueryInvoker would have to be a universal type [T <: Product] forAll { type T } which is not supported by Scala.
By using an explicit type parameter, queryToQueryInvoker can be instantiated with that specific type. The method can still return List[_ <: Product] because List is co-variant in its element type.
Edit: It is possible after all. You have to move the existential to the right place:
class PagingListModel(... query: Query[Projection[T]] forSome { type T <: Product }) {
def getPageData(): List[_ <: Product] = ... {
val i = queryToQueryInvoker(query.drop(itemStartNumber).take(pageSize))
i.list
}
}
Without the extra variable i the compiler will infer the wrong type and then complain about it. Looks like a bug to me.
My knowledge of ScalaQuery is limited, but it looks as though you should be parameterizing the class.
class PagingListModel[T <: Product] (
startPageNumber: Int,
pageSize: Int,
query: Query[Projection[T]]
) extends AbstractListModel {
...
}
Existentials can be tricky, and are best avoided if at all possible.