Scala: Extract types from generic parameters - scala

I have a class like this:
abstract class Foo[I, T, A <: Bar[I, T]](x: SomeClass[A]){
When I want to inherit class Foo, I've to specify types T and I, which could be extracted from type parameters of type A. (I.e. there are enough data to extract these types.) Does the Scala compiler allow to extract them somehow? I'd like to write something like:
abstract class Foo[A <: Bar[_, _]](x: SomeClass[A]){
type Bar[I, T] = A // <-- something like pattern matching
It is strange that I can write that, but the type Bar[I, T] = A line does not seem to declare anything. The line passes, but I can use neither type I nor type T.
Can I do something similar?
I know I could use abstract class Foo[I, T](x: SomeClass[A]){ and then define type A = Bar[I, T], but it loses some universality. Additionaly, this case means more (boilerplate) code for the code users, because they are likely to define a shortcut (i.e. type alias) for Bar[I, T].
I can rewrite the abstract class Foo to a trait and I probably will do so. But I am not sure if it could help.

When type parameters are going to be unhandy one should give abstract types a try:
scala> class Bar[I, T](i: I, t: T)
defined class Bar
scala> class SomeClass[A](a: A)
defined class SomeClass
scala> trait Trait { type I; type T; type A = Bar[I,T] }
defined trait Trait
scala> class Foo(x: SomeClass[Foo#A]) extends Trait { type I = String; type T = String }
defined class Foo
scala> new Foo(new SomeClass(new Bar("", ""))) // works
res0: Foo = Foo#7e1f613c
scala> new Foo(new SomeClass(new Bar("", 0))) // does not work
<console>:12: error: type mismatch;
found : Int(0)
required: String
new Foo(new SomeClass(new Bar("", 0)))
^

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.

Relationship between parameterized class with different type, Scala

In Scala,
What is the relationship between parameterized class with different type?
For example,
class A[T]
What is the relationship between A[Int] (or A[String]) and A?
and what is the relationship between A[Int] and A[String] ?
I want to know, because I would like to do something like
case class A[T](v: T) { def print = println(v) }
def iter(s: Seq[A[???]]) = s.map(print) // What is the proper annotation of '???'
iter(Seq(A(1), A("Hello")) // 1 Hello
What is the relationship between A[Int] and A?
A is a type constructor of kind * → *, and A[Int] is one of the possible results of apply a type (Int) to the type constructor A.
What is the relationship between A[Int] and A[String]?
The least upper bound between these two types is A[_ >: String with Int], which can only be instantiated to A[Any] since it's the only super class of String and Int.
What is the proper annotation of '???'
In your example that would be Any, or a type parameter you could add to your def iter that would itself be instantiated to Any.
Types are related using Variance.
To answer your question, Java's wildcards equivalent in Scala is Existential types. So you can specify something like this:
def iter(s: Seq[A[_]]) = s.map(_.print)
which is equivalent to:
def iter(s: Seq[A[T] forSome {type T}]) = s.map(_.print)
You can also specify bounds like below:
def iter(s: Seq[A[_ <: CharSequence]]) = s.map(_.print)
println(iter(Seq(A[StringBuilder](new StringBuilder("Test")), A[String]("Hello"))))
[Explicit type specified in constructors to avoid implicit conversion to required type CharSequence].
Note that the following would not compile though:
def iter(s: Seq[A[CharSequence]]) = s.map(_.print)
println(iter(Seq(A[StringBuilder](new StringBuilder("Test")), A[String]("Hello"))))
This is where variance specification in class would help:
case class A[+T](v: T) {
def print = v
}
def iter(s: Seq[A[CharSequence]]) = s.map(_.print)
println(iter(Seq(A[StringBuilder](new StringBuilder("Test")), A[String]("Hello"))))

Scala: using type parameters or abstract types as type bounds

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.

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>

why trait method needs asInstanceOf and class method don't

I have defined the following trait:
trait Felem[T <: Felem[T]] {
def mul(that: T): T
def square: T = this.mul(this.asInstanceOf[T])
}
I also define a class based on this trait:
class F2elem(val coef: Boolean) extends Felem[F2elem] {
override def square: F2elem = this.mul(this)
...
}
My questions are about the need of "asInstanceOf" in the definition of the "square" method in the trait. If I remove it, I get the following error:
error: type mismatch;
found : Felem.this.type (with underlying type Felem[T])
required: T
def square: T = this.mul(this)
Why is it needed in the trait ?
Why it is not needed in the class ?
Does it cost anything in term of execution time or memory ?
The parameter of mult must be of type T.
When calling mul(this), the this parameter is of type Felem[T], which is not and does not conform to T. There is the additional constraint that T conforms to Felem[T]. But this is not what you want, you would need the opposite, Felem[T] to conform to T.
On the other hand, in F2elem, T is exactly F2elem, so it typechecks (completley unrelated to one being a trait and the other one a class)
Here is example to show that the definition in Felem must indeed not typecheck, and that it is possible to have implementors where Felem[T] does not conform to T.
class F3elem extends Felem[F2elem] // this is 2, not 3
This declaration is correct, F2elem which is given for T satisfies T <: Felem[T].
However, an inherited this.mul(this) in square would be invalid, mult expect a T, that is F2elem, and this is F3elem. And they are unrelated.
What you probably want is that every Felem must be like F2elem, that is that T must be the type of the actual class. You can enforce this with a self type.
trait Felem [T <: Felem[T]] { this: T => /* your code */ }
When you write that, you state that in every implementation, the type of the implementation must conform to T. Doing that, it will typecheck, and you will not be allowed to instanciate F3elem above :
error: illegal inheritance; self-type F3elem does not conform to
Felem[F2elem]'s selftype F2elem
class F3elem extends Felem[F2elem] {
1) In your trait this is not an instance of T:
scala> trait Felem[T <: Felem[T]] {
| def mul(that: T): T = that
| def square: T = this.mul(this.asInstanceOf[T])
| }
defined trait Felem
scala> class F2elem extends Felem[F2elem]
defined class F2elem
scala> class F3elem extends Felem[F2elem]
defined class F3elem
scala> new F3elem()
res1: F3elem = F3elem#2e0b08f1
scala> res1.square
java.lang.ClassCastException: F3elem cannot be cast to F2elem
2) In your class this is F2elem and T == F2elem, so this is T.
You're using T as your method's parameter type. This means that whatever the type of T is will be the type that's required in the method (hence not needing the cast in the class, since its type is what is signified by T originally).
If you change the type of that to Felem[T], you will not need the cast.