Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.with alternative in scala

Tags:

scala

I come from Groovy and it has a .with method on every type which accepts a single-argument closure; the argument is the object on which the .with method is being called. This allows a very cool technique of extending the functional chaining capabilities, which releases you from obligation to introduce temporary variables, factors your code, makes it easier to read and does other niceties.

I want to be able to do something like this:

Seq(1, 2, 3, 4, 5)
  .filter(_ % 2 == 0)
  .with(it => if (!it.isEmpty) println(it))

Instead of

val yetAnotherMeaninglessNameForTemporaryVariable = 
  Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0)
if (!yetAnotherMeaninglessNameForTemporaryVariable.isEmpty) 
  println(yetAnotherMeaninglessNameForTemporaryVariable)

In other words in the first example the .with is kinda similar to .foreach but instead of iterating thru the items of the object it is being called once on the object itself. So it is equal to Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0).

Since I was very surprised not to find anything like that in Scala, my questions are:

  • am I missing something?
  • are there any alternative techniques native to Scala?
  • if not, are there any decent reasons why this feature is not implemented in Scala?

Update: An appropriate feature request has been posted on the Scala issue tracker: https://issues.scala-lang.org/browse/SI-5324. Please vote and promote

like image 793
Nikita Volkov Avatar asked Dec 16 '11 17:12

Nikita Volkov


2 Answers

There doesn't exist any such method in the standard library, but it's not hard to define your own.

implicit def aW[A](a: A) = new AW(a)
class AW[A](a: A) {
  def tap[U](f: A => U): A = {
    f(a)
    a
  }
}

val seq = Seq(2, 3, 11).
          map(_ * 3).tap(x => println("After mapping: " + x)).
          filter(_ % 2 != 0).tap(x => println("After filtering: " + x))

EDIT: (in response to the comment)

Oh, I misunderstood. What you need is there in the Scalaz library. It comes under name |> (referred to as pipe operator). With that, your example would look like shown below:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) |> { it => if(!it.isEmpty) println(it) }

If you cannot use Scalaz, you can define the operator on your own:

implicit def aW[A](a: A) = new AW(a)
class AW[A](a: A) {
  def |>[B](f: A => B): B = f(a)
}

And it's not a bad practice to pimp useful method(s) on existing types. You should use implicit conversions sparingly, but I think these two combinators are common enough for their pimps to be justifiable.

like image 115
missingfaktor Avatar answered Sep 23 '22 11:09

missingfaktor


There is some syntax for this pattern included in Scala:

Seq(1, 2, 3, 4, 5).filter(_ % 2 == 0) match { case it => if (!it.isEmpty) println(it) }

However, this is no accepted idiom so you should maybe refrain from (ab)using it.

If you dislike inventing loads and loads of names for dummy variables, remember that you can use scope braces:

val importantResult = {
  val it = Seq(1,2,3).filter(_ % 2 == 0)
  if (!it.isEmpty) println(it)
  it
}

val otherImportantResultWithASpeakingVariableName = {
  val it = // ...
  /* ... */
  it
}
like image 45
Debilski Avatar answered Sep 24 '22 11:09

Debilski