Mikaël Mayer
Mikaël Mayer

Reputation: 10701

Retrieve type parameter in pattern matching in scala

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) }
                ^

Upvotes: 3

Views: 421

Answers (2)

Shyamendra Solanki
Shyamendra Solanki

Reputation: 8851

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

Upvotes: 1

Randall Schulz
Randall Schulz

Reputation: 26486

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 */
  }

Upvotes: 0

Related Questions