Scala: passing function parameters as tuple - scala

Let's say I have:
def foo(i: Int, s: String)
and have:
val tuple: (Int, String) = (1, "s")
can I pass tuple to foo without adding a wrapper for foo?

Yes, its possible. Using .tupled one can convert the lambda into accepting the tuple as argument.
Scala REPL
scala> def foo(i: Int, s: String): Int = i
foo: (i: Int, s: String)Int
scala> (foo _).tupled
res3: ((Int, String)) => Int = scala.Function2$$Lambda$226/234698513#45984654
scala> val tuple: (Int, String) = (1, "s")
tuple: (Int, String) = (1,s)
scala> (foo _).tupled(tuple)
res5: Int = 1

foo(tuple._1, tuple._2) should work.
If you want something more maintainable, I would recommend following:
type MyTuple = (Int, String)
def foo(t:MyTuple) = ??? // some code
val tuple = (1, "s")
foo(tuple) // works
Also inside foo, the best way to unwrap the tuple would be
val (int, string) = t

Related

How to find instance of a value type in Scala?

I know isInstanceOf can be used to find value type, but how do I find the type of 'str'?
What type is it?
scala> val str = ("Scala", "Elixir","Spark")
str: (String, String, String) = (Scala, Elixir, Spark)
The following throws an error (exclude Any/AnyRef etc for now):
scala> str.isInstanceOf[List]
<console>:13: error: type List takes type parameters
str.isInstanceOf[List]
scala> str.isInstanceOf[String]
<console>:13: warning: fruitless type test: a value of type (String, String, String) cannot also be a String (the underlying of String)
str.isInstanceOf[String]
^
res9: Boolean = false
I can check it this way but is there a name for this?
scala> str.isInstanceOf[(String, String, String)]
res12: Boolean = true
Use :type in scala repl to find type
Actual type is Tuple3[String, String, String]
str.isInstanceOf[Tuple3[String, String, String]]
Scala REPL
scala> val str = ("Scala", "Elixir","Spark")
str: (String, String, String) = (Scala,Elixir,Spark)
scala> :type str
(String, String, String)
scala> str.isInstanceOf[Tuple3[String, String, String]]
res2: Boolean = true
scala> str.getClass
res3: Class[_ <: (String, String, String)] = class scala.Tuple3
How to determine val type programmatically?
For instance, you want to know the type of the value is some specific type?
// assign x value
val x: Any = "this is string"
// custom function evaluate type
def f[T](v: T) = v match {
case _: Int => println("Int")
case _: String => println("String")
case _ => println("Unknown")
}
// determine val type
f(x)
Yet another way to determine a type programmatically is using Manifest:
scala> def getType[T: Manifest](t: T): Manifest[T] = manifest[T]
getType: [T](t: T)(implicit evidence$1: Manifest[T])Manifest[T]
scala> val str = ("Scala", "Elixir","Spark")
str: (String, String, String) = (Scala,Elixir,Spark)
scala> getType(str)
res0: Manifest[(String, String, String)] = scala.Tuple3[java.lang.String, java.lang.String, java.lang.String]
The modern (meaning Scala 2.10 or later), Scala way of programmatically getting the compile time type of something is using a TypeTag.
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> def getType[T: TypeTag](value: T) = typeOf[T]
getType: [T](value: T)(implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.Type
scala> val str = ("Scala", "Elixir","Spark")
str: (String, String, String) = (Scala,Elixir,Spark)
scala> println(getType(str))
(java.lang.String, java.lang.String, java.lang.String)
scala> getType(str) <:< typeOf[(String,String,String)]
res1: Boolean = true
scala> getType((1,2,3)) <:< typeOf[(String,String,String)]
res2: Boolean = false
getClass will give your the erased runtime class. isInstanceOf[T] will test whether the erased runtime class is the same as or a subclass of the erased runtime class of T.
And "erased" means that the following evaluates to true.
(1,2,3).isInstanceOf[(String,String,String)]
"runtime" and "compile time" mean that this is true:
val a: Any = (1,2,3)
a.isInstanceOf[(_,_,_)]
while this is false:
val a: Any = (1,2,3)
getType(a) <:< typeOf[(Int,Int,Int)]
This method can help you get the type of any val/var at runtime, it's also works in compiled code.
import scala.reflect.runtime.universe._
def printType[T](x: T)(implicit tag: TypeTag[T]): Unit = println(tag.tpe.toString)
printType(List[Int](1,2,3)) // output: List[Int]
printType(("xxx", 123, 0.1)) // output: (String, Int, Double)
printType(2) // output: Int

Tuples as Key in Map, how to retrieve value?

Imagine the following (pseudo) class:
class Board(rows: Int, cols: Int)
{
val fields = Map[(Int, Int), Field] // Position is a tuple
def getField(position: (Int, Int)): Field
{
fields(position)
}
}
How could I do this? I could create a type like:
type Position = (Int, Int)
and then val fields = Map[Position, Field]
Could that work?
Your code has syntax errors, but once you fix them, it should work. Using String instead of Field (since I don't know the definiton of Field), you can write this in Scala REPL and see it work:
scala> val fields: Map[(Int, Int), String] = Map((1,2) -> "a", (3,4) -> "b")
fields: Map[(Int, Int),String] = Map((1,2) -> a, (3,4) -> b)
scala> def getField(position: (Int, Int)) = fields(position)
getField: (position: (Int, Int))String
scala> getField((1,2))
res1: String = a

Why does this partial application not compile?

The following:
val add = (a: Int, b: Int) => a + b
gets converted to:
object add extends Function2[Int, Int, Int] {
def apply(a: Int, b: Int) = a + b
}
while
val a1 = add(_: Int, 3)
gets converted to:
object a1 extends Function1[Int, Int] {
def apply(x: Int): Int = {
add(x, 3)
}
}
But when I do:
scala> val a2 = add _
a2: () => (Int, Int) => Int = <function0>
And then call a2, it throws an error:
scala> a2(1, 2)
<console>:11: error: too many arguments for method apply: ()(Int, Int) => Int in trait Function0
a2(1, 2)
^
Why is this? Why does the following work?
a2()(1, 2)
add is already a Function2[Int, Int, Int]. If you want a2 to have the same type, then a simple assignment will suffice.
scala> val a2 = add
a2: (Int, Int) => Int = <function2>
scala> a2(1, 2)
res3: Int = 3
What you're thinking of is eta-expansion of a method into a function. If we had:
def add(a: Int, b: Int): Int = a + b
Then, we would use add _ to get the eta-expansion to assign to a value.
scala> def a2 = add _
a2: (Int, Int) => Int
scala> a2(1, 2)
res4: Int = 3
But add is already a function, so the underscore has a different meaning. add is now a value and not a method. Since add is a value, it is like a parameter-less method that returns a Function2[Int, Int, Int]. And if we try to get the eta-expansion of that, we get () => Function2[Int, Int, Int].
Consider a simpler example where we have a simple val a = 1. a is essentially the same as a parameter-less method that returns 1 (def a = 1). If I try to obtain the eta-expansion, I will get () => Int.
scala> val a = 1
a: Int = 1
scala> val a2 = a _
a2: () => Int = <function0>

Scala - tupled and partially applied functions

I'm fairly new to Scala and functional programming in general so I'm having a bit trouble wrapping my head around the concept of partially applied functions and function currying. There's also a pretty high chance that I'm gonna mix up some terminology, so all corrections are appreciated.
Note: I'm using the Scala Play framework but this is more of a Scala problem than a Play problem.
Given a function like this
def create(id: Long, userId: Long, label: String)
I get the userId and label as a (Int, String) tuple and the id as a Long. Basically what I'm trying to do is passing the id and the tuple to the function at the same time.
Now, I've read that passing a tuple to a function can be done something like this
scala> def f(a: Int, b: String) = 0
f: (a: Int, b: String)Int
scala> (f _).tupled((2, "Hello"))
res0: Int = 0
and that it is also possible to partially apply a function like this
scala> def f(a: Int, b: String) = 0
f: (a: Int, b: String)Int
scala> val ff = f(_: Int, "Hello")
ff: Int => Int = <function1>
scala> ff(2)
res1: Int = 0
So my initial idea was to combine these two concepts something like this
scala> def g(a: Long, b: Int, c: String) = 0
g: (a: Long, b: Int, c: String)Int
scala> val h = g(1, _: Int, _: String)
h: (Int, String) => Int = <function2>
scala> (h _).tupled((2, "Hello"))
However this results in an error
<console>:10: error: _ must follow method; cannot follow h.type
(h _).tupled((1, "Hello"))
^
So my question is first of all why doesn't this work because to me this makes sense. And secondly how would I go about achieving this effect?
Thanks for your help!
Just don't abstract it twice: don't do redundand underscore to the h as it's already a function after partial-applying:
scala> def create(a: Long, b: Int, c: String) = 0
create: (a: Long, b: Int, c: String)Int
scala> val h = create(1, _: Int, _: String)
h: (Int, String) => Int = <function2>
scala> h.tupled((1, "Hello"))
res0: Int = 0
More deeply, tupled is defined on functions (means object of Function, like Int => String), not on methods (like def f(i: Int): String) - so sometimes you need to convert a method to the function - it's called eta-expansion (or eta-abstraction more generally). When you do partial applying - eta-expansion is done automatically (you already have a (Int, String) => Int) - so you don't have to do it twice.
See The differences between underscore usage in these scala's methods, Difference between method and function in Scala for more information.

How to flatten a nested tuple?

I have a nested tuple structure like (String,(String,Double)) and I want to transform it to (String,String,Double). I have various kinds of nested tuple, and I don't want to transform each manually. Is there any convenient way to do that?
If you use shapeless, this is exactly what you need, I think.
There is no flatten on a Tupple. But if you know the structure, you can do something like this:
implicit def flatten1[A, B, C](t: ((A, B), C)): (A, B, C) = (t._1._1, t._1._2, t._2)
implicit def flatten2[A, B, C](t: (A, (B, C))): (A, B, C) = (t._1, t._2._1, t._2._2)
This will flatten Tupple with any types. You can also add the implicit keyword to the definition. This works only for three elements. You can flatten Tupple like:
(1, ("hello", 42.0)) => (1, "hello", 42.0)
(("test", 3.7f), "hi") => ("test", 3.7f, "hi")
Multiple nested Tupple cannot be flatten to the ground, because there are only three elements in the return type:
((1, (2, 3)),4) => (1, (2, 3), 4)
Not sure about the effiency of this, but you can convert Tuple to List with tuple.productIterator.toList, then flatten the nested lists:
scala> val tuple = ("top", ("nested", 42.0))
tuple: (String, (String, Double)) = (top,(nested,42.0))
scala> tuple.productIterator.map({
| case (item: Product) => item.productIterator.toList
| case (item: Any) => List(item)
| }).toList.flatten
res0: List[Any] = List(top, nested, 42.0)
Complement of answer above
Paste this utility code:
import shapeless._
import ops.tuple.FlatMapper
import syntax.std.tuple._
trait LowPriorityFlatten extends Poly1 {
implicit def default[T] = at[T](Tuple1(_))
}
object flatten extends LowPriorityFlatten {
implicit def caseTuple[P <: Product](implicit lfm: Lazy[FlatMapper[P, flatten.type]]) =
at[P](lfm.value(_))
}
then you are able to flatten any nested tuple:
scala> val a = flatten(((1,2),((3,4),(5,(6,(7,8))))))
a: (Int, Int, Int, Int, Int, Int, Int, Int) = (1,2,3,4,5,6,7,8)
Note that this solution does not work for self-defined case class type, which would be converted to String in the output.
scala> val b = flatten(((Cat("c"), Dog("d")), Cat("c")))
b: (String, String, String) = (c,d,c)
In my opinion simple pattern matching would work
scala> val motto = (("dog", "food"), "tastes good")
val motto: ((String, String), String) = ((dog,food),tastes good)
scala> motto match {
| case ((it, really), does) => (it, really, does)
| }
val res0: (String, String, String) = (dog,food,tastes good)
Or if you have a collection of such tuples:
scala> val motto = List(
| (("dog", "food"), "tastes good")) :+ (("cat", "food"), "tastes bad")
val motto: List[((String, String), String)] = List(((dog,food),tastes good), ((cat,food),tastes bad))
scala> motto.map {
| case ((one, two), three) => (one, two, three)
| }
val res2: List[(String, String, String)] = List((dog,food,tastes good), (cat,food,tastes bad))
I think it would be convenient even if you have several cases.