MatchError instead of compile error while trying to extract values from a TupleN with invalid N?

116 views
Skip to first unread message

Emre Çelikten

unread,
Dec 5, 2013, 7:48:30 AM12/5/13
to scala...@googlegroups.com
Hello everyone,

I hope that this is the correct list for this post.

Consider the following snippet:

def f(a: String): (Some[String], Boolean) = (Some(a), false)

val (a1: Option[String], a2: Option[String], a3: Boolean) = if (true) f("a") else (None, None, false)


Strangely, it compiles but throws a MatchError exception when run: scala.MatchError: (Some(a),false) (of class scala.Tuple2)

Return type seems to be checked at runtime - at least that is what I understood from running the code in the REPL with -print:

[[syntax trees at end of                   cleanup]] // <console>
package $line26 {
  object $read extends Object {
    def <init>(): $line26.$read.type = {
      $read.super.<init>();
      ()
    }
  };
  object $read$$iw$$iw extends Object {
    <synthetic> private[this] val x$1: Tuple3 = _;
    private[this] val a1: Option = _;
    <stable> <accessor> def a1(): Option = $read$$iw$$iw.this.a1;
    private[this] val a2: Option = _;
    <stable> <accessor> def a2(): Option = $read$$iw$$iw.this.a2;
    private[this] val a3: Boolean = _;
    <stable> <accessor> def a3(): Boolean = $read$$iw$$iw.this.a3;
    def <init>(): type = {
      $read$$iw$$iw.super.<init>();
      $read$$iw$$iw.this.x$1 = {
        case <synthetic> val x1: Object = ($line25.$read$$iw$$iw.f("a"): Object);
        case8(){
          if (x1.$isInstanceOf[Tuple3]())
            {
              <synthetic> val x2: Tuple3 = (x1.$asInstanceOf[Tuple3](): Tuple3);
              {
                val a1: Object = x2._1();
                val a2: Object = x2._2();
                val a3: Object = x2._3();
                if (a1.$isInstanceOf[Option]())
                  {
                    <synthetic> val x3: Option = (a1.$asInstanceOf[Option](): Option);
                    if (a2.$isInstanceOf[Option]())
                      {
                        <synthetic> val x4: Option = (a2.$asInstanceOf[Option](): Option);
                        if (a3.$isInstanceOf[Boolean]())
                          {
                            <synthetic> val x5: Boolean = (scala.Boolean.unbox(a3): Boolean);
                            matchEnd7(new Tuple3(x3, x4, scala.Boolean.box(x5)))
                          }
                        else
                          case9()
                      }
                    else
                      case9()
                  }
                else
                  case9()
              }
            }
          else
            case9()
        };
        case9(){
          matchEnd7(throw new MatchError(x1))
        };
        matchEnd7(x: Tuple3){
          x
        }
      };
      $read$$iw$$iw.this.a1 = $read$$iw$$iw.this.x$1._1().$asInstanceOf[Option]();
      $read$$iw$$iw.this.a2 = $read$$iw$$iw.this.x$1._2().$asInstanceOf[Option]();
      $read$$iw$$iw.this.a3 = scala.Boolean.unbox($read$$iw$$iw.this.x$1._3());
      ()
    }
  };
  object $read$$iw extends Object {
    def <init>(): type = {
      $read$$iw.super.<init>();
      ()
    }
  }
}


On the other hand, these ones fail to compile as expected:

val (a1: Option[String], a2: Option[String], a3: Boolean) = f("a")

val (a1: Option[String], a2: Option[String], a3: Boolean) = if (true) f("a") else (None, false)


My question is, if I am correct, why is this being checked at runtime? We know that the function f returns a Tuple3. Shouldn't there be a compile error for this case?

Thanks in advance,

Emre

Simon Ochsenreither

unread,
Dec 5, 2013, 9:24:02 AM12/5/13
to scala...@googlegroups.com
Hi Emre,

the issue here is that it's not just a simple assignment, but you are actually pattern matching.

Nonetheless, the compiler should probably reject it.

It already does in other cases:

scala> val (a: Int, b: Int) = if (true) (1,2,3) else (1,2,3)
<console>:7: error: constructor cannot be instantiated to expected type;
 found   : (T1, T2)
 required: (Int, Int, Int)
       val (a: Int, b: Int) = if (true) (1,2,3) else (1,2,3)
           ^


I think the issue here is caused because the compiler computes the common type of the if expression and because Tuple2 and Tuple3 don't really have an useful common type (the rhs has a supertype of the lhs), the compiler can't tell whether the pattern match is non-sense.

Naftoli Gugenheim

unread,
Dec 8, 2013, 8:05:32 PM12/8/13
to Simon Ochsenreither, scala-user

I agree. I don't think there's anything the compiler can do short of determining an expected type based on the pattern match. As far as I know the compiler only does the reverse.

As an illustration:
val a = if (true) 1 else "" // Any
x match { case s: String => s } // can only fail at runtime. You're allowed to test if an Any is a String.

val i: Int = if (true) "" else 2 // here it's the right hand side that's invalid because the compiler knows its expected type.

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
Reply all
Reply to author
Forward
0 new messages