Boris van Katwijk
Boris van Katwijk

Reputation: 3188

Map an instance using function in Scala

Say I have a local method/function

def withExclamation(string: String) = string + "!"

Is there a way in Scala to transform an instance by supplying this method? Say I want to append an exclamation mark to a string. Something like:

val greeting = "Hello"
val loudGreeting = greeting.applyFunction(withExclamation) //result: "Hello!"

I would like to be able to invoke (local) functions when writing a chain transformation on an instance.

EDIT: Multiple answers show how to program this possibility, so it seems that this feature is not present on an arbitraty class. To me this feature seems incredibly powerful. Consider where in Java I want to execute a number of operations on a String:

appendExclamationMark(" Hello! ".trim().toUpperCase()); //"HELLO!"

The order of operations is not the same as how they read. The last operation, appendExclamationMark is the first word that appears. Currently in Java I would sometimes do:

Function.<String>identity()
    .andThen(String::trim)
    .andThen(String::toUpperCase)
    .andThen(this::appendExclamationMark)
    .apply(" Hello "); //"HELLO!"

Which reads better in terms of expressing a chain of operations on an instance, but also contains a lot of noise, and it is not intuitive to have the String instance at the last line. I would want to write:

" Hello "
    .applyFunction(String::trim)
    .applyFunction(String::toUpperCase)
    .applyFunction(this::withExclamation); //"HELLO!"

Obviously the name of the applyFunction function can be anything (shorter please). I thought backwards compatibility was the sole reason Java's Object does not have this. Is there any technical reason why this was not added on, say, the Any or AnyRef classes?

Upvotes: 2

Views: 80

Answers (3)

Boris van Katwijk
Boris van Katwijk

Reputation: 3188

A while after asking this question, I noticed that Kotlin has this built in:

inline fun <T, R> T.let(block: (T) -> R): R

Calls the specified function block with this value as its argument and returns its result.

A lot more, quite useful variations of the above function are provided on all types, like with, also, apply, etc.

Upvotes: 0

Alexey Romanov
Alexey Romanov

Reputation: 170723

If you want to apply any functions (anonymous, converted from methods, etc.) in this way, you can use a variation on Yuval Itzchakov's answer:

object Combinators {
  implicit class Combinators[A](val x: A) {
    def applyFunction[B](f: A => B) = f(x)
  }
}

Upvotes: 1

Yuval Itzchakov
Yuval Itzchakov

Reputation: 149538

You can do this with an implicit class which provides a way to extend an existing type with your own methods:

object StringOps {
  implicit class RichString(val s: String) extends AnyVal {
    def withExclamation: String = s"$s!" 
  }

  def main(args: Array[String]): Unit = {
    val m = "hello"
    println(m.withExclamation)
  }
}

Yields:

hello!

Upvotes: 5

Related Questions