3

I already found that it is possible to bind the type parameter in a pattern matching

Why does not that work in that case ?

trait T

case class S[A](a: A) extends T

def pr(t1: T, t2: T) = (t1, t2) match {
  case (S(a): S[ta], S(b): S[tb]) => println(a); println(b)
}
                         ^
error: '=>' expected but ':' found.

For information, this works:

def pr(t1: T, t2: T) = (t1, t2) match {
  case (s1: S[a], s2: S[b]) => println(s1.a); println(s2.a)
}

And this as well:

def pr(t1: T, t2: T) = (t1, t2) match {
  case (S(a), S(b)) => println(a); println(b)
}

I need to recover the type to define other functions whose type cannot be inferred because in the context of eta-expansion.

UPDATE

As mentionned in the comments, I need the types just for correct type checking, not anything else.

For example:

trait T
case class S[A](a: A, w: A => Int) extends T
def makeTwo(t1: T, t2: T) = (t1, t2) match {
  case (S(a1, w1), S(a2, w2)) =>
    val wNew = { (a, b) => w1(a) + w2(b) }
    S((a, b), wNew)
}

error: missing parameter type
  val wNew = { (a, b) => w1(a) + w2(b) }
                ^
Community
  • 1
  • 1
Mikaël Mayer
  • 10,425
  • 6
  • 64
  • 101
  • You can't retrieve type parameter info because it doesn't exist at runtime - see type erasure – Luigi Plinge Dec 02 '13 at 14:05
  • 1
    @LuigiPlinge, type erasure here does not really matter. The author does not need to obtain real type, he needs to bind a type in order for the code to pass the type checking. I also had to do something like this when I was writing generic [de]serialization library, however, I somehow had avoided this problem. – Vladimir Matveev Dec 02 '13 at 14:36
  • Try putting a type annotation in for the `makeTwo` method and you'll see the problem. A `T` in this scheme can be absolutely anything – Luigi Plinge Dec 02 '13 at 22:40
  • In this case, T is not a type variable, it is really a trait. Should I make it sealed ? – Mikaël Mayer Dec 03 '13 at 10:00

2 Answers2

1

Quoting from Scala syntax rules:

varid ::= lower idrest

idrest ::= {letter | digit} [‘_’ op]

op ::= opchar {opchar}

opchar ::= “all other characters in \u0020-007F and Unicode
            categories Sm, So except parentheses ([]) and periods”

// 'a' and 'a_-' is valid, 'a(' is not.

Rule for Typed Pattern match says:

Pattern1 ::= varid ‘:’ TypePat
           | ‘_’   ‘:’ TypePat

So

def pr(list: Any) = list match {

   case a :String => // works
   case a_- :String => // works a_- is valid instance of 'varid'
   case a() :String => // does not work.
   case a(b) :List[Int] // does not work either!
}

Hence:

case S(a): S[ta]  is not valid syntax for pattern match.

However following is valid

case (s1 :S[a], s2: S[b])

according to Tuple Pattern matching rule:

SimplePattern ::= ‘(’ [Patterns] ‘)’
Patterns ::= Pattern {‘,’ Patterns}

Apart from the two possible options that you have listed, you may also use:

 case tup: (S[_], S[_]) => 

Following seems to work:

trait T

case class S[A](a: A, w: A => Int) extends T

def makeTwo(t1: T, t2: T) = (t1, t2) match {
  case (S(a1, w1), S(a2, w2)) =>
      val wNew = {  tup:(Any, Any) => w1(tup._1) + w2(tup._2) }
      S((a1, a2), wNew)
}

def someString (s: String) = { s.length }

val twoS = makeTwo(S("Hello", someString), S("World!", someString))
println(twoS.w(twoS.a))  // gives 11
Shyamendra Solanki
  • 8,751
  • 2
  • 31
  • 25
  • ":11: warning: non-variable type argument S[ta] in type pattern (S[ta], S[tb]) is unchecked since it is eliminated by erasure case tup: (S[ta], S[tb]) =>" And when I continue: :12: error: missing parameter type for expanded function The argument types of an anonymous function must be fully known. (SLS 8.5) Expected type was: ? val wNew = {case (a: ta, b: tb) => tup._1.w(a) + tup._2.w(b) } – Mikaël Mayer Dec 03 '13 at 10:08
  • @MikaëlMayer see the updated version, with wNew defined as taking param of (Any, Any) – Shyamendra Solanki Dec 03 '13 at 11:02
0

In at least some circumstances you can get what you want.

If you add a context bound (syntactic sugar for an implicit parameters) of the type ClassTag to the type parameter you want enforced by the compiler, it will have what it needs to do that.

E.g., I have a generic bit of Akka plumbing I used to buffer messages between producers and consumers which I want to be type-safe.

The class is defined like this:

class   TimedBatchQueue[T : ClassTag](batchLimit:   Int,
                                      MaxQueueTime: FiniteDuration,
                                      receiver:     ActorRef)
extends Actor

The receive function includes a case for the incomming messages it is to buffer:

  def receive = {
    ...
    case mesg: T => /* Enqueue the message */
    case mesg    => /* Bounce the unacceptable message back to the sender encapsulated in a reject message */
  }
Randall Schulz
  • 26,420
  • 4
  • 61
  • 81