1

I have the following simple demo code, which give me the type mismatch error. I quite do not understand why this error happens. Would anyone show me how to fix this error. Further explain or related learning resource would be great thankful.

object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[T <: TypeHolder[_]](val holder: T) {

  def showElementType(t: T#ElementType): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

The compile error as:

Error:(10, 26) type mismatch;
 found   : Application.foo.type (with underlying type Foo)
 required: _$1
  client.showElementType(foo)

UPDATE at 2018-03-15

Just like Andrey Tyukin would be curious about what TypeHolder is used for, it contains nothing beyond T. I add some function on this trait.

Codes as follow.

object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T

  def holderFun(): Unit = println(s"holder, type:${getClass}")

  def elementFun(ele: ElementType): Unit = println(s"element, type:${getClass}")
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[T <: TypeHolder[_]](val holder: T) {

  def showElementType(t: T#ElementType): Unit = {
    println("show element type " + t.toString)
    holder.holderFun()
    holder.elementFun(t)
  }

}

which gives compile errors as below:

Error:(10, 26) type mismatch;
 found   : o2.Application.foo.type (with underlying type o2.Foo)
 required: _$1
  client.showElementType(foo)
Error:(30, 23) type mismatch;
 found   : _$1
 required: Client.this.holder.ElementType
    (which expands to)  _$1
    holder.elementFun(t)

On the whole, The core thing I want to know is:

when I instantiate client via val client = new Client(holder) while holder has the type: TypeHolder[Foo] Does scala compiler can infer the ElementType of hoder is "Foo"?

Thanks so much @Andrey for giving various workable solutions. I exam your code snippet 1~3, and compare with my code carefully, which makes me more clear now. However I still have some questions.

Version 3

This version of your code is closest to my original post code. The only difference is the type restriction of function showElementType

  • Mine: def showElementType(t: T#ElementType): Unit
  • Your: def showElementType(t: holder.ElementType): Unit

Which confuse me is, since "(t: holder.ElementType)" works, "(t: T#ElementType)" should work as well? And actually it isn't. why?

Version 2

The difference here is the location of type ElementType = T. I put it in the trait definition, while you put it in type parameter restriction.

  • Mine: trait TypeHolder[T] { type ElementType = T}
  • Your: class Client[T, H <: TypeHolder { type ElementType = T }]

I cannot understand why the assignment statement in trait definition isn't works.

Version 1

You use two type parameter T & H to capture the type of Foo and TypeHolder respectively. It should absolutely works. The type of client is also corresponding to Client[Foo, TypeHolder[Foo]]. My question is, is it possible to define client use only one type parameter, e.g. Client[TypeHolder[Foo]]. In other words, is it possible for the compiler to infer from the TypeHolder[Foo] that the ElementType is Foo?

Thank you.

user5698801
  • 576
  • 4
  • 10
  • 1
    This looks like an [`XY`-problem](https://en.wikipedia.org/wiki/XY_problem), and the answers so far seem different enough that one might assume that the answerers are trying to guess the `X`-part at random. For example, I've proposed to get rid of `TypeHolder`, while @MarkoŠvaljek has proposed not to use the `Foo`-typed `foo` as argument to `showElementType`. While all those solution proposals might compile, the meaning of the resulting programs seems rather different. Further clarification about the intended `X` is required. – Andrey Tyukin Mar 15 '18 at 01:56

2 Answers2

2

This compiles after some modifications:

object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  // not sure why you try to use this one
  // client.showElementType(foo)
  // foo doesn't extend the Typeholder, it's just regular class

  // on the other hand this will compile
  client.showElementType(holder)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[T <: TypeHolder[_]](val holder: T) {

  // no need to use the #ElementType
  def showElementType(t: T): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

Hope this also helps:

What does the `#` operator mean in Scala?

https://github.com/ghik/opinionated-scala/wiki/Generics-and-type-members

As far as the error _$1 goes, it's just compiler saying: "I can't figure this one out"

Marko Švaljek
  • 2,071
  • 1
  • 14
  • 26
  • If one assumes that there is no need to use `ElementType`, then what was the whole question all about in the first place? Calling `.toString` and `.getClass` on an argument of a completely unconstrained type `T`? It doesn't use either `ElementType` or the `TypeHolder` in any significant way, and it passes the `holder` itself instead of `foo` to `showElementType`, while the `foo` remains an unused variable. – Andrey Tyukin Mar 14 '18 at 17:18
  • Hey Andrey, thanks for commenting. To be honest I tried to provide further resources about `#` and show that it might not even be needed in this case – Marko Švaljek Mar 14 '18 at 22:17
  • The linked resources are fine. I just wondered why you decided to change the code inside `App`: I would assume that the code inside `App` represents what the OP wanted to achieve, and the whole type-level computations around it are there only to support the correctness of the code inside `App`. But, indeed, it's kind-of hard to tell whether that's exactly what the OP wanted... I've requested further clarification of the question. – Andrey Tyukin Mar 15 '18 at 02:06
  • Hi Marko, thanks for the further resource. However, pass `holder` to `client.showElementType()` seems not compile as well. – user5698801 Mar 15 '18 at 08:55
  • no problem man, I also did some modifications to the method in order to make it compile (the code I provided compiles). Again, I'm not 100% sure of the original intent of the code so I just went for first reasonable approach. I also find the answer from Andrey very good ;) – Marko Švaljek Mar 15 '18 at 10:18
2

I'm not sure what you tried here, but here is my version of "probably closest projection of the code into compilable Scala":

import scala.language.higherKinds
object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[T, H[X] <: TypeHolder[X]](val holder: H[T]) {
  def showElementType(t: T): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

but it might as well be this:

import scala.language.higherKinds
object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client[Foo, FooTypeHolder](holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder {
  type ElementType
}

class FooTypeHolder extends TypeHolder {
  type ElementType = Foo
}

class Client[T, H <: TypeHolder { type ElementType = T }](val holder: H) {
  def showElementType(t: T): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

or maybe this?:

import scala.language.higherKinds
object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder[T] {
  type ElementType = T
}

class FooTypeHolder extends TypeHolder[Foo]

class Client[H <: TypeHolder[_]](val holder: H) {
  def showElementType(t: holder.ElementType): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

What I fundamentally don't understand about your code is: what is the TypeHolder[T] supposed to do, exactly? The whole TypeHolder[T] construction doesn't seem to contain any information beyond T itself, so why not use T directly, like this?:

object Application extends App {
  private val foo = new Foo
  private val client = new Client[Foo]
  client.showElementType(foo)
}

class Foo

class Client[T] {
  def showElementType(t: T): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

Some general hints:

  • Avoid asking multiple questions at once
  • Avoid asking multiple questions about one of the answers to you previous question in the same posting.
  • This probably gonna require some heavy editing...

Edit

I'll just try to answer all additional questions in more or less the same order as you've added them to your posting:

1. It works with holder.ElementType, because when you instantiate Client, the holder is known to be of type TypeHolder[Foo], therefore we know holder.ElementType = Foo.

2. It does not work with T#ElementType for T <: TypeHolder[_], because, as the existential in the signature says, T can be a TypeHolder forSome unspecified ElementType:

T <: TypeHolder[_$1] forSome { type _$1 }

T#ElementType 
  = (TypeHolder[_])#ElementType
  = (TypeHolder[_$1] forSome { type _$1 })#ElementType 
  = _$1

thus T#ElementType is set to some weird synthetic existential type _$1, and you get the error message that looks somewhat like this:

typeHolder.scala:6: error: type mismatch;
  found   : Main.foo.type (with underlying type Foo)
  required: _$1
  client.showElementType(foo)
                         ^

So, setting it to an existential type is completely useless, you will never be able to find a term that satisfies this weird type constraint. You can't even cast into this type by force, because this type doesn't even have a name by which it can be referenced anywhere in code.

To be honest: this seems like complete garbage. This whole projection shouldn't compile at all, why in the world would anyone want an existentially quantified type to escape from under its quantifier? This probably should be reported as a compiler issue.

3. The definition

class Client[T, H <: TypeHolder { type ElementType = T }]

works because it also keeps the type T explicit. If you replaced it by an existential

class Client[T, H <: TypeHolder { type ElementType = X forSome { type X }}]

then again every connection between T and TypeHolder would be lost, and it would fail for very much the same reason as T#ElementType above: you just can't replace the element type by some unknown existential and then hope to be able to pass Foo to it.

4. On "is it possible to define client use only one type parameter?" -- of, course, why not?

import scala.language.higherKinds
object Application extends App {
  private val foo = new Foo
  private val holder = new FooTypeHolder
  private val client = new Client(holder)
  client.showElementType(foo)
}

class Foo

trait TypeHolder {
  type ElementType
}

class FooTypeHolder extends TypeHolder {
  type ElementType = Foo
}

class Client[H <: TypeHolder](val holder: H) {
  def showElementType(t: H#ElementType): Unit = {
    println("show element type " + t.toString)
    println(t.getClass)
  }
}

Just don't add any type parameters to TypeHolder, so you don't have to erase them by existentials later.

Andrey Tyukin
  • 43,673
  • 4
  • 57
  • 93
  • Awesome, all your code are compile success. Thanks so much for giving various workable solutions. The `TypeHolder` is a simplified version, I can add some functions on it. Due to number of the words limitation in comment, I update my questions. Would you please give me more guidance. Thank you. – user5698801 Mar 15 '18 at 09:03
  • @user5698801 I've heavily modified my answer. I think the last code snippet does exactly what you wanted. You've just confused yourself too much with all the type parameters and existentials. If you don't add any type parameters to `TypeHolder`, and then don't add any existentials, then everything *just works*. – Andrey Tyukin Mar 15 '18 at 12:57
  • WOW!!! Your explanation for existential type is so wonderful. I learn a lot from you, I also believe other people can benefit from your answer. Thank you so much for your heavy patience. This must be the correct answer. – user5698801 Mar 16 '18 at 01:23
  • @user5698801 Ok. Would you mind if to my "heavy patience" I add some "heavy editing skills", and rewrite both the question and the answer a little bit? As it is now, it's a little difficult for other people to benefit from it, because there is quite a bit of unnecessary sprawl. I'll throw out some unnecessary digressions, and focus on the interesting parts that lead to the solution (I hope this is the solution, right?). – Andrey Tyukin Mar 16 '18 at 03:02