It's not a particular known fact that one of the things it does is extending the class with the trait Product. This trait makes it easy to access the elements of the class, like in the example below:
scala> trait recordType {
| self : Product =>
| def records : String = (
| for(parm <- self.productIterator)
| yield parm.asInstanceOf[AnyRef].getClass.getSimpleName+": "+parm
| ) mkString "\n"
| }
defined trait recordType
scala> case class X(i : Int, c : Char, s : String) extends recordType
defined class X
scala> val x = X(5, 'd', "this")
x: X = X(5,d,this)
scala> println(x.records)
Integer: 5
Character: d
String: this
Let me talk about the trait recordType a bit first. Its body first line declares that "self : Product =>". This has two purposes. First, it creates an alias for "this". Second, and most important, it tells the compiler that to be able to use this trait, a class must mix in the trait Product.
Anyway, the code works fine for simple classes, but it might be weird for collections:
scala> case class Y(a : Array[String], l : List[Int]) extends recordType
defined class Y
scala> val y = Y(Array("this", "example"), List(1,2,3))
y: Y = Y([Ljava.lang.String;@60a517,List(1, 2, 3))
scala> println(y.records)
String[]: [Ljava.lang.String;@60a517
$colon$colon: List(1, 2, 3)
Here, Array is mucked up because Java's Array are mucked up. As for the List, any non-empty List actually belongs to class "::", a subclass of List.