This page is no longer maintained — Please continue to the home page at www.scala-lang.org

How to do safe type-casting (and string conversion) from array to tuple?

2 replies
tolsen77
Joined: 2008-10-08,
User offline. Last seen 1 year 38 weeks ago.
I'm trying to cleanup some code where I from Javascript do calls to Scala by passing a message and an array of arguments to a common function that do the casting and the actual call from Scala.

The old version was something like:

message match {
  case "MessageA" =>
    obj.functionA(args(0).asInstanceOf[String], args(1).asInstanceOf[String].toDouble, args(2).asInstanceOf[String].toDouble, args(3).asInstanceOf[String].toDouble, args(4).asInstanceOf[String].toDouble, args(5).asInstanceOf[String].toDouble, args(6).asInstanceOf[String].toInt)
 case "MessageB" =>
 ...

After adding some casting functions it removed the casting exception:

def unpackArgs[A](array: Array[AnyRef]): Option[Tuple1[A]]
def unpackArgs[A,B](array: Array[AnyRef]): Option[Tuple2[A,B]]
...
def unpackArgs[A,B,C,D,E,F,G](array: Array[AnyRef]): Option[Tuple7[A,B,C,D,E,F,G]]

message match {
  case "MessageA" =>
    unpackArgs[String,String,String,String,String,String,String](jsargs) match {
      case Some(args) => obj.functionA(args._1, args._2.toDouble, args._3.toDouble, args._4.toDouble, args._5.toDouble, args._6.toDouble, args._7.toInt)
      case None => // error
 case "MessageB" =>
 ...

This still give errors when converting the strings, so is it in any way possible to use the type system with .toInt .toDouble to make a safe generic casting function to tuples for those also (assuming strings are given)? Also, I tried to use pattern matching with lists, but specifying the type on elements gives errors.
geoff
Joined: 2008-08-20,
User offline. Last seen 1 year 25 weeks ago.
Re: How to do safe type-casting (and string conversion) from ar

On Thu, Apr 02, 2009 at 10:27:21PM +0200, Trond Olsen said
> I'm trying to cleanup some code where I from Javascript do calls to Scala by
> passing a message and an array of arguments to a common function that do the
> casting and the actual call from Scala.
>
> [snip]
>
> message match {
> case "MessageA" =>
> unpackArgs[String,String,String,String,String,String,String](jsargs)
> match {
> case Some(args) => obj.functionA(args._1, args._2.toDouble,
> args._3.toDouble, args._4.toDouble, args._5.toDouble, args._6.toDouble,
> args._7.toInt)
> case None => // error
> case "MessageB" =>
> ...
>
> This still give errors when converting the strings, so is it in any way
> possible to use the type system with .toInt .toDouble to make a safe generic
> casting function to tuples for those also (assuming strings are given)?
> Also, I tried to use pattern matching with lists, but specifying the type on
> elements gives errors.

This is a good time to use extractors. Extractors are like backwards
functions that can be used in pattern matching. They take a value that
would be the result of the forward function and return the input that
would result in that output (if any). Here's how you might be able to
use them::

object IntValue {
def apply(x: Int) = x.toString
def unapply(x: AnyRef): Option[Int] = x match {
case s:String => try { Some(s.toInt) } catch { case _ => None }
case _ => None
}
}

object DoubleValue {
def apply(x: Double) = x.toString
def unapply(x: AnyRef): Option[Double] = x match {
case s:String => try { Some(s.toDouble) } catch { case _ => None }
case _ => None
}
}

object StringValue {
def apply(x: String) = x
def unapply(x: AnyRef): Option[String] = x match {
case s:String => Some(s)
case _ => None
}
}

// ...

(message, jsargs) match {
case ("MessageA",Seq(StringValue(x), StringValue(y), IntValue(z))) =>
// types are x: String, y: String, z: Int
obj.functionA(x,y,z)
case ("MessageB",Seq(IntValue(x), DoubleValue(y), StringValue(z))) =>
// types are x: Int, y: Double, z: String
obj.functionB(x,y,z)
case _ =>
error("Invalid call: "+ message +"("+ jsargs.mkString(", ") +")")
}

There's some information on extractors in the Scala tour at
http://www.scala-lang.org/node/112

tolsen77
Joined: 2008-10-08,
User offline. Last seen 1 year 38 weeks ago.
Re: How to do safe type-casting (and string conversion) from a
Well, that solved everything! And now it got a single point for failures also. Thank you.

On Thu, Apr 2, 2009 at 11:09 PM, Geoff Reedy <geoff@programmer-monk.net> wrote:
On Thu, Apr 02, 2009 at 10:27:21PM +0200, Trond Olsen said
> I'm trying to cleanup some code where I from Javascript do calls to Scala by
> passing a message and an array of arguments to a common function that do the
> casting and the actual call from Scala.
>
> [snip]
>
> message match {
>    case "MessageA" =>
>     unpackArgs[String,String,String,String,String,String,String](jsargs)
> match {
>       case Some(args) => obj.functionA(args._1, args._2.toDouble,
> args._3.toDouble, args._4.toDouble, args._5.toDouble, args._6.toDouble,
> args._7.toInt)
>       case None => // error
>  case "MessageB" =>
>  ...
>
> This still give errors when converting the strings, so is it in any way
> possible to use the type system with .toInt .toDouble to make a safe generic
> casting function to tuples for those also (assuming strings are given)?
> Also, I tried to use pattern matching with lists, but specifying the type on
> elements gives errors.

This is a good time to use extractors. Extractors are like backwards
functions that can be used in pattern matching. They take a value that
would be the result of the forward function and return the input that
would result in that output (if any). Here's how you might be able to
use them::

object IntValue {
 def apply(x: Int) = x.toString
 def unapply(x: AnyRef): Option[Int] = x match {
   case s:String => try { Some(s.toInt) } catch { case _ => None }
   case _ => None
 }
}

object DoubleValue {
 def apply(x: Double) = x.toString
 def unapply(x: AnyRef): Option[Double] = x match {
   case s:String => try { Some(s.toDouble) } catch { case _ => None }
   case _ => None
 }
}

object StringValue {
 def apply(x: String) = x
 def unapply(x: AnyRef): Option[String] = x match {
   case s:String => Some(s)
   case _ => None
 }
}

// ...

(message, jsargs) match {
 case ("MessageA",Seq(StringValue(x), StringValue(y), IntValue(z))) =>
   // types are x: String, y: String, z: Int
   obj.functionA(x,y,z)
 case ("MessageB",Seq(IntValue(x), DoubleValue(y), StringValue(z))) =>
   // types are x: Int, y: Double, z: String
   obj.functionB(x,y,z)
 case _ =>
   error("Invalid call: "+ message +"("+ jsargs.mkString(", ") +")")
}

There's some information on extractors in the Scala tour at
http://www.scala-lang.org/node/112

Copyright © 2012 École Polytechnique Fédérale de Lausanne (EPFL), Lausanne, Switzerland