Scala; Can you define many parameters as one - scala

Can you define a set of variables for later use?
Here are some pseudo code highlighting my intent:
def coordinates = x1, y1, x2, y2
log("Drawing from (%4.1f, %4.1f) to (%4.1f, %4.1f)".format(coordinates))
canvas.drawLine(coordinates, linePaint)
Here is a working example that contains duplicated code.
log("Drawing from (%4.1f, %4.1f) to (%4.1f, %4.1f)".format(x1, y1, x2, y2))
canvas.drawLine(x1, y1, x2, y2, linePaint)

Yes, you can, although the syntax is arguably horribly clunky, and there are some limitations that may seem a little arbitrary at first. The trick is to convert the method to a function (called "eta expansion"), and then to use that function's tupled method to get something you can apply to a tuple.
Suppose you have a class like this:
class Foo {
def f(a: String, b: String) = "%s, %s".format(b, a)
def g(x: Int, y: Int, z: Int) = x + y * z
}
And an instance:
val foo = new Foo
And some data you'd like to use Foo's methods on:
val names = ("John", "Doe")
val nums = (42, 3, 37)
You can't just write foo.f(names) or foo.g(nums), because the types don't line up—argument lists and tuples are different things in Scala. But you can write the following:
scala> (foo.f _).tupled(names)
res0: String = Doe, John
scala> (foo.g _).tupled(nums)
res1: Int = 153
Sticking the underscore after the method turns it into a function (this is in my opinion the most confusing little quirk of Scala's syntax), and tupled converts it from a function with two (or three) arguments to a function with a single tuple argument.
You could clean the code up a little by defining the following helper functions, for example:
scala> val myF = (foo.f _).tupled
myF: ((String, String)) => String = <function1>
scala> val myG = (foo.g _).tupled
myG: ((Int, Int, Int)) => Int = <function1>
scala> myF(names)
res2: String = Doe, John
scala> myG(nums)
res3: Int = 153
I'm not sure that's much better, though.
Lastly, you can't (conveniently) use this approach on a varargs method—you can't for example write the following:
val coordsTupleToString = ("(%4.1f, %4.1f) to (%4.1f, %4.1f)".format _).tupled
Or even just:
val coordsToString = "(%4.1f, %4.1f) to (%4.1f, %4.1f)".format _
Which is yet another reason to avoid varargs in Scala.

Looks like you need a tuple:
val coordinates = (x1, y1, x2, y2)
or maybe a full-blown object?

Now, this may be obvious, but if it annoys you in only a few cases, you can always enhance:
implicit def enhancedCanvas(canvas: Canvas) = new {
// using bad and slow syntax. please change this in Scala 2.10.
def drawLineC(coordinates: (Float, Float, Float, Float), paint: Paint) = {
val (x1, y1, x2, y2) = coordinates
canvas.drawLine(x1, y1, x2, y2, paint)
}
}
Another possibility, if you’re crazy enough. (Might be that an enhancement like this is already in Scalaz or Shapeless.)
implicit def enhTuple4[A,B,C,D](t: Tuple4[A,B,C,D]) = new {
def |<[E] (f: (A, B, C, D) => E) = f(t._1, t._2, t._3, t._4)
}
// to be used as
val coordinates = (x1, y1, x2, y2)
coordinates |< (canvas.drawLine(_, _, _, _, linePaint))

Related

Passing arguments to higher order functions

how do I pass different parameters for my function inside a higher order function. my code below.
val sum = (a: Int, b:Int) => a + b
val x = 7
val y = 9
def sq (x: Int, y: Int, f: (Int, Int)=> Int): Int = {
x + y * f(a,b)
}
println(sq(x,y,sum(2,3)))
This doesn't work and throws an error that 'a' is not defined.
The parameter list for sq looks ok.
The call to sq isn’t right, the function would get evaluated before it gets passed in. Assuming it got by the compiler, which of course it doesn’t.
Passing in a function does not mean its arguments come along for the ride. Passing in a function means the caller may not know what arguments to use or even if the function should be called. The sq function has to supply arguments when it calls it. a and b mean nothing outside the definition of sum. sq could supply x and y as arguments to sum.
Just as an addition to #Nathan answer. This example would make sense:
val sum = (a: Int, b:Int) => a + b
def sq (x: Int, y: Int, f: (Int, Int)=> Int): Int = {
x + y * f(x, y)
}
val x = 7
val y = 9
println(sq(x,y,sum))
This would be evaluated in your sgfunction like:
7 + 9 * sum(7, 8)
I hope this makes sense.
In addition to the #Nathan Hughes answer, if you want to pass the different argument to the sum function on each call you need to change your sq method like
def sq (x: Int, y: Int, f: (Int, Int)=> Int): (Int, Int)=> Int = {
x + y * f(_: Int, _:Int)
}
Insted of expecting the int from the sq method return partially applied function of type (Int, Int) => Int from `sq' then pass other argument to that function like,
sq(10, 20, sum)(1, 2) // 1st approach
//2nd approach would be
//You can hold the function in another variable and call that function with other arguments
val partialSum = sq(10,20, sum)
partialSum(1, 2)
and you will get your result.
Or if you still want that sq method should return Int, you can define your sq method like
def sq (x: Int, y: Int, f: (Int, Int)=> Int)(a:Int, b:Int):Int = {
x + y * f(a, b)
}
scala> sq(10,20, sum)(1,2)
res2: Int = 70

How do I call a function with params (x: A*)(y: A*)?

Suppose I have a function that looks like this:
def foo(x: Int*)(y: Int*): Int = ???
How can I pass Arrays of x and y to foo?
val x = Array(4,6,3,7)
val y = Array(3,4,6,3)
foo(x, y) // Error:Type mismatch
Use :_* to tell the compiler to unpack the sequence to match the expected varargs input. Also, since foo is declared using two parameter lists, calling the function has to match:
foo(x: _*)(y: _*)

How to make Scalacheck arbString generate only readable Strings?

Postgres doesn't accept all kind of symbols that Scalacheck arbString generates. Is there a way to generate human readable strings with Scalacheck?
If you take a look at the Gen object you can see a few generators, including alphaChar and identifier.
scala> import org.scalacheck.Gen._
import org.scalacheck.Gen._
scala> identifier.sample
res0: Option[String] = Some(vxlgvihQeknhe4PolpsJas1s0gx3dmci7z9i2pkYlxhO2vdrkqpspcaUmzrxnnb)
scala> alphaChar.sample
res1: Option[Char] = Some(f)
scala> listOf(alphaChar).sample
res2: Option[List[Char]] = Some(List(g, n, x, Y, h, a, c, e, a, j, B, d, m, a, r, r, Z, a, z, G, e, i, i, v, n, Z, x, z, t))
scala> listOf(alphaChar).map(_.mkString).sample
res3: Option[String] = Some(oupwJfqmmqebcsqbtRxzmgnJvdjzskywZiwsqnkzXttLqydbaahsfrjqdyyHhdaNpinvnxinhxhjyzvehKmbuejaeozytjyoyvb)
You can do so by adding a case class ReadableChar(c: Char), and defining an instance of arbitrary for it. Maybe something like
case class ReadableChar(c: Char)
implicit val arbReadable: Arbitrary[ReadableChar] = Arbitrary {
val legalChars = Range('a', 'z').map(_.toChar)
for {
c <- Gen.oneOf(legalChars)
} yield ReadableChar(c)
}
Then you can use the instance for Arbitrary[Array[ReadableChar]] to generate an array of readable chars, turn it into a string via .map(_.c).toString.
This works if you want to define "human readable strings" by the chars they are allowed to contain. If you need additional restrictions you can write a second case class ReadableString(s: String) and define an instance of Arbitrary for it, too.

For Comprehension Example to Understand Composing Monads

J. Abrahamson provided an in-depth answer to my composing-monads-v-applicative-functors question.
I gained some intuition, but I do not fully understand his helpful answer.
Given the following Either's:
scala> x
res0: Either[String,Int] = Right(100)
scala> err
res1: Either[Boolean,Int] = Left(false)
I tried to chain them together:
scala> for {
| xx <- x.right
| yy <- err.right
| } yield xx + yy
res3: scala.util.Either[Any,Int] = Left(false)
But, of course I don't want an Either[Any, Int]. Yet we get Any since, as I understand, the parent of Either[String, Int] and Either[Boolean, Int] is Either[Any, Int].
When building a for-comprehension, is the typical approach to find an end-type, i.e. Either[String, Int], and then make each flatMap call have that type?
If you mean that you want Either[Boolean, Int] instead of Either[Any, Int], then "You can't always get what you want". Composition of same (as in your example) Either types (but some other values) may return String instead of Boolean even for right projection:
scala> val x: Either[String,Int] = Left("aaa")
x: Either[String,Int] = Left(aaa)
scala> val r: Either[Boolean,Int] = Right(100)
r: Either[Boolean,Int] = Right(100)
scala> for {
| xx <- x.right
| yy <- r.right //you may swap xx and yy - it doesn't matter for this example, which means that flatMap could be called on any of it
| } yield yy + xx
res21: scala.util.Either[Any,Int] = Left(aaa)
So, Either[Any,Int] is really correct and smaller as possible end-type for it.
The desugared version:
scala> x.right.flatMap(xx => r.right.map(_ + xx))
res27: scala.util.Either[Any,Int] = Left(aaa)
Monad's flatMap signature:
flatMap[AA >: A, Y](f: (B) ⇒ Either[AA, Y]): Either[AA, Y]
Passed types:
flatMap[Any >: String, Int](f: (Int) ⇒ Either[?Boolean?, Int]): Either[Any, String]
AA >: A is completely legal here as it allows f = r.right.map(_ + xx) return left type bigger than String (so ?Boolean? becomes Any), otherwise it wouldn't even work. Answering your question, flatMap can't have AA = Boolean here, because A is already at least String, and as shown in first example can actually be a String.
And, by the way, there is no monads composition in this example - r could be just a functor. More than that, you're composing RightProjection with RightProjection here, so they commute automatically.
The only way to inferr Boolean is to kill String type with Nothing - you can do that only if you sure that x is always Right:
scala> val x: Either[Nothing,Int] = Right(100)
x: Either[Nothing,Int] = Right(100)
scala> val r: Either[Boolean,Int] = Left(false)
r: Either[Boolean,Int] = Left(false)
scala> for {
| yy <- r.right
| xx <- x.right
|
| } yield yy + xx
res24: scala.util.Either[Boolean,Int] = Left(false)
Then, of course you can't put strings, which is totally correct.

Refactoring a small Scala function

I have this function to compute the distance between two n-dimensional points using Pythagoras' theorem.
def computeDistance(neighbour: Point) = math.sqrt(coordinates.zip(neighbour.coordinates).map {
case (c1: Int, c2: Int) => math.pow(c1 - c2, 2)
}.sum)
The Point class (simplified) looks like:
class Point(val coordinates: List[Int])
I'm struggling to refactor the method so it's a little easier to read, can anybody help please?
Here's another way that makes the following three assumptions:
The length of the list is the number of dimensions for the point
Each List is correctly ordered, i.e. List(x, y) or List(x, y, z). We do not know how to handle List(x, z, y)
All lists are of equal length
def computeDistance(other: Point): Double = sqrt(
coordinates.zip(other.coordinates)
.flatMap(i => List(pow(i._2 - i._1, 2)))
.fold(0.0)(_ + _)
)
The obvious disadvantage here is that we don't have any safety around list length. The quick fix for this is to simply have the function return an Option[Double] like so:
def computeDistance(other: Point): Option[Double] = {
if(other.coordinates.length != coordinates.length) {
return None
}
return Some(sqrt(coordinates.zip(other.coordinates)
.flatMap(i => List(pow(i._2 - i._1, 2)))
.fold(0.0)(_ + _)
))
I'd be curious if there is a type safe way to ensure equal list length.
EDIT
It was politely pointed out to me that flatMap(x => List(foo(x))) is equivalent to map(foo) , which I forgot to refactor when I was originally playing w/ this. Slightly cleaner version w/ Map instead of flatMap :
def computeDistance(other: Point): Double = sqrt(
coordinates.zip(other.coordinates)
.map(i => pow(i._2 - i._1, 2))
.fold(0.0)(_ + _)
)
Most of your problem is that you're trying to do math with really long variable names. It's almost always painful. There's a reason why mathematicians use single letters. And assign temporary variables.
Try this:
class Point(val coordinates: List[Int]) { def c = coordinates }
import math._
def d(p: Point) = {
val delta = for ((a,b) <- (c zip p.c)) yield pow(a-b, dims)
sqrt(delta.sum)
}
Consider type aliases and case classes, like this,
type Coord = List[Int]
case class Point(val c: Coord) {
def distTo(p: Point) = {
val z = (c zip p.c).par
val pw = z.aggregate(0.0) ( (a,v) => a + math.pow( v._1-v._2, 2 ), _ + _ )
math.sqrt(pw)
}
}
so that for any two points, for instance,
val p = Point( (1 to 5).toList )
val q = Point( (2 to 6).toList )
we have that
p distTo q
res: Double = 2.23606797749979
Note method distTo uses aggregate on a parallelised collection of tuples, and combines the partial results by the last argument (summation). For high dimensional points this may prove more efficient than the sequential counterpart.
For simplicity of use, consider also implicit classes, as suggested in a comment above,
implicit class RichPoint(val c: Coord) extends AnyVal {
def distTo(d: Coord) = Point(c) distTo Point(d)
}
Hence
List(1,2,3,4,5) distTo List(2,3,4,5,6)
res: Double = 2.23606797749979