2

I think I may be doing something very silly with this but following snippet doesn't compile.

scala> case class Foo1(i: Int)
defined class Foo1

scala> trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
defined trait Foo1

And then implementing a trait as follow

scala> implicit val foodVal = new Foo1{
 |       def testFoo[Foo1](l:List[Foo1] ) = {
 |         for{
 |           a <- l
 |           if (a.i == 1)
 |         } yield a
 |       }
 |     }
<console>:13: error: value i is not a member of type parameter Foo1

Update Thanks for the responses. I am genuinely interested in understanding difference between passing a type at a trait level vs def level. in the answers I can see type is passed at a trait level which does work but following still gives me the same error

scala> trait Bar { def testFoo[T](l1: List[T] ) : List[T] }
defined trait Bar

scala> implicit val fooVal = new Bar {
 | override def testFoo[Foo1](l:List[Foo1]) : List[Foo1] = {
 | for { a <- l if a.i == 1} yield a
 | }
 | }
<console>:18: error: value i is not a member of type parameter Foo1 

2 Answers 2

4

At a REPL, the definition:

trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }

is replacing the class definition

case class Foo1(i: Int)

If you try to do the same thing using the paste command you will see that both cannot be defined together:

scala> :paste
// Entering paste mode (ctrl-D to finish)

case class Foo1(i: Int)
trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }


// Exiting paste mode, now interpreting.

<console>:36: error: Foo1 is already defined as case class Foo1
       trait Foo1{def testFoo[T](l1 : List[T] ) : List[T] }
             ^

As such you need to use a different name for your trait than your case class, which is sensible, as they refer to different things.

Once renamed, there are a couple of ways to achieve your goals. One is outlined by Sascha (use a type member), or you can promote type T to parameterize your trait instead of parameterizing the method on the trait.

case class Foo1(i: Int)

trait Bar[T] {
  def testFoo(l1 : List[T] ) : List[T]
}

implicit val foodVal: Bar[Foo1] = new Bar[Foo1] {
  override def testFoo(l: List[Foo1]): List[Foo1] = {
    for { a <- l if a.i == 1 } yield a
  }
}

To answer the Update question, the answer is the difference between declaration and implementation.

When I write an implementation of a parameterized trait in the form below, I am saying "replace the abstract type T with the concrete type Foo".

trait Bar[T] {...}

val instance = new Foo[Bar] { ... }

But when I override a method that is parameterized by type and change the name of the type, all I am doing is specifying a different name for that abstract type. I'm not specifying the specific concrete class to use, I'm specifying a new symbol by which to identify the abstract type that I am operating on.

trait Bar {
    def something[T](t: T) : T
}

val instance = new Bar {
    def something[Foo](t: Foo) : Foo = { ... }
}

So, in the example above, Foo is not a concrete type Foo, rather Foo is just a symbol that means an arbitrary type which will be made concrete based on the type of parameter passed to the method.

As such, even if there is a concrete type called Foo in the same scope, it is shadowed by the type parameter Foo that has been introduced and any reference to Foo, or an instance of Foo, within the context of that method, is a reference to the abstract type parameter Foo, not the concrete type Foo.

Sign up to request clarification or add additional context in comments.

2 Comments

thanks. I updated my question. would you mind explaining question in "Update" section?
Added an update which hopefully addresses your question. BTW - a separate question might have been better, because it can be difficult for future readers, and people searching for answers, to figure out what is going on when reading back on multiple updates to questions and answers.
3
  1. You have to name the trait and the case class differently form each other as two types cannot have the same name. (Mike already explained, why it compiles in REPL)

  2. In the line def testFoo[Foo1](l: List[Foo1]) = {, Foo1 is a new generic type parameter and NOT the type of your case class Foo1.

  3. (IMHO) It is always a good idea to annotate an implicit val with a type.

That said, here is code that should do, what you want:

case class Foo1(i: Int)

trait Foo2 {
  type T

  def testFoo(l1 : List[T] ) : List[T]
}

implicit val foodVal: Foo2 = new Foo2 {
  type T = Foo1

  def testFoo(l: List[T]) = {
    for { a <- l if a.i == 1 } yield a
  }
}

Update (in answer to the questions Update):

Type parameters, especially in naming, work very similar to usual parameters. The key to whether a class parameter is accessed or a identically named function parameter is the scope.

class Foo(arg: Int) {
  val field: Int = 1

  def func(field: String) = {
    // here field will always refer to the function paramter
    // if you want to access the class field you would have to call this.field
  }
}

class Bar[T] {
  def func[T](t: T) = {
    // within the scope of the function T IS NOT "Bar#T".
    // However, you cannot access the class type parameter with "this.T" in this example 
  }

  def func2(t: T) = {
    // here T is the classes type parameter T
  }
}

To complete the example with access by this.

class Bar[T] {
  type Inner = T
  def func[Inner](t: Inner) = {
    // won't compile because the "Inner" type of the class is initialized with T
    val test: this.Inner = t
  }
}

If you do not specify a type parameter further it can be Any(thing). If you need a type with a specific API. You need to tell the compiler that with the <: operator.

trait Api {
  def func: String
}

def func[T <: Api](arg: T) = {
  arg.func // yay
}

This is called an upper type bound. There are more ways of arbitrary complexity to refine a type parameter, for which you will have to read the documentation.

2 Comments

Your first example won't work as written, because the type T <: Foo1 is not compatible with the method being overridden as the type is more specific. Your second example will work fine. Voted up regardless because it is a good answer.
@SaschaKolberg thanks. I updated my question. would you mind explaining question in "Update" section?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.