flybonzai
flybonzai

Reputation: 3931

How to use Type Parameter to handle any types in a tuple?

I'm working on a problem from the book Learning Scala where it asks you to create a function where the input is a 2-tuple, and if either value is an int you return the tuple with the int in the first position. I have something that works if they are both the same type, but cannot figure out how to make the types flexible enough to accept any input and then handle the return value possibly being switched. I come from a Python background, so I'm just learning how to enforce such things:)

def intInFirst[A](x: (A, A)): (A, A) = {
  if (x._2.isInstanceOf[Int]) (x._2, x._1)
  else x
}

val x = (0, 0) // works
val y = ("hello", 15) // fails, should return (15, "hello")

Upvotes: 0

Views: 887

Answers (2)

evan.oman
evan.oman

Reputation: 5562

Repeat of my comment: by saying x: (A, A) you are saying that the input tuple x must have the same type in both positions. Thus it cannot accept ("hello", 15) because the types are different.

Now to actually solve the problem, the only solution I can think of is to use the return type (Any, Any):

scala> def intInFirst[A, B](x: (A, B)): (Any, Any) = 
     | x match {
     | case (_, b: Int) => x.swap
     | case _ => x
     | }
intInFirst: [A, B](x: (A, B))(Any, Any)

scala> intInFirst((1,2))
res2: (Any, Any) = (2,1)

scala> intInFirst((1,"apple"))
res4: (Any, Any) = (1,apple)

scala> intInFirst(("apple",1))
res5: (Any, Any) = (1,apple)

Also as a tip: most times where you find yourself about to use isInstanceOf or asInstanceOf think about what you are trying to accomplish and consider using pattern matching instead.

Edit:

This probably isn't what the question is going for but you could also use an Either for the return type:

scala> :pa
// Entering paste mode (ctrl-D to finish)

def intInFirst[A, B](x: (A, B)): Either[(B, A), (A, B)] =
    x match {
        case (_, b: Int) => Left(x.swap)
        case _ => Right(x)
    }

// Exiting paste mode, now interpreting.

intInFirst: [A, B](x: (A, B))Either[(B, A),(A, B)]

scala> intInFirst((1,"apple"))
res1: Either[(String, Int),(Int, String)] = Right((1,apple))

scala> intInFirst((1,2))
res2: Either[(Int, Int),(Int, Int)] = Left((2,1))

Either is interesting because you can then pattern match on the output to find out which case was used:

scala> :pa
// Entering paste mode (ctrl-D to finish)

intInFirst((1,2)) match {
    case Left(a) => s"This value was swapped: $a"
    case Right(a) => s"This value was not swapped: $a"
}

// Exiting paste mode, now interpreting.

res3: String = This value was swapped: (2,1)

scala> :pa
// Entering paste mode (ctrl-D to finish)

intInFirst((1,"apple")) match {
    case Left(a) => s"This value was swapped: $a"
    case Right(a) => s"This value was not swapped: $a"
}

// Exiting paste mode, now interpreting.

res4: String = This value was not swapped: (1,apple)

Upvotes: 1

jqno
jqno

Reputation: 15520

The type (A, A) implies that both elements of the tuple have the same type. In other words, it will accept (0, 0) and ("hello", "world") but not ("hello", 15). You can fix that by using a type (A, B).

After that, the cleanest and easiest way to see if either element is an Int, is probably by using a pattern match instead of an .isInstanceOf[Int] check.

Upvotes: 3

Related Questions