0
scala> val names = List("Peter", "Paul", "Mary")
names: List[String] = List(Peter, Paul, Mary)

scala> names.map(_.toUpperCase)
res12: List[String] = List(PETER, PAUL, MARY)

In this case, the underscore represents the only input argument, which is the element of names. This string is implicitly converted to StringOps and toUpperCase is invoked.

However, this does not work:

scala> names.map(StringOps.toUpperCase _)
<console>:14: error: value toUpperCase is not a member of object scala.collection.immutable.StringOps
       names.map(StringOps.toUpperCase _)

I thought that this syntax is how I would get a reference to the function from the toUpperCase method.

allstar
  • 1,155
  • 4
  • 13
  • 29
  • 1
    I tried your code,but got a different error. that was error: not found: value StringOps. StringOps doesn't have a companion object. String to StringOps is using implicit def augmentString(x: String): StringOps defined in Predef . – jiayp89 Nov 06 '17 at 01:44

3 Answers3

2

First of all, there is no implicit conversion for _.toUppercase that convert String to StringOps, Since toUppercase is belong to String type, it's unnecessary convert to StringOps.

so for _.toUppercase is actual expand to high order function: val a: String => String = (str: String) => str.toUpperCase.

and StringOps implicit conversion defined in Predef.scala: augmentString, and this conversion only will occur when used StringOps's method, like: slice, stripSuffix, stripPrefix etc, for Example:

  "name".slice(0, 2)    // convert "name" to new StringOps("name").slice(0,2)
  "name".stringPrefix
chengpohi
  • 14,064
  • 1
  • 24
  • 42
1

StringOps is a class, hence to use its method you'll need to instantiate an instance:

names.map( x => (new StringOps(x)).toUpperCase )

Or

names.map( new StringOps(_) toUpperCase )
Leo C
  • 22,006
  • 3
  • 26
  • 39
  • Thank you for the response. So this works, but I thought that you need the underscore to tell Scala to convert the method toUpperCase into a function? So, for example `names.map( x => (new StringOps(x)).toUpperCase _)` – allstar Nov 06 '17 at 22:49
  • 1
    `x => (new StringOps(x)).toUpperCase` is already a function-type argument for the `map` function, hence there is no need (and wouldn't make sense) to further eta-expand `toUpperCase`. Usage and syntax of `_` in Scala can be confusing at times. This [SO link](https://stackoverflow.com/questions/8000903/what-are-all-the-uses-of-an-underscore-in-scala) about the various `_` syntax might be of interest. – Leo C Nov 07 '17 at 00:41
  • Ah, right! So I I guess my questions are - "1. Is there any way to write this using eta expansion syntax with underscore instead of explicitly expanding out the function? 2. If not, what makes this different than the cases where you can?" – allstar Nov 07 '17 at 17:18
  • 1
    The `placeholder` syntax used in my 2nd suggested solution is in essence equivalent to an eta-expansion in which `new StringOps(_) toUpperCase` gets expanded to `x => (new StringOps(x)).toUpperCase`. More examples can be found [here](https://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#method-values). – Leo C Nov 08 '17 at 02:01
  • Every example I've seen that demonstrates eta expansion has the underscore _following the method name_. In this case, it seems like the underscore is being used as the one parameter in the right-hand side of a function `x => (new StringOps(x)).toUpperCase` which is why it's OK, but that seems to me like a different use? Even zero-parameter method examples I've seen have the trailing underscore. However, the examples I've seen use methods from the same class or from a package object - so in this case, using a regular member method might be different? – allstar Nov 08 '17 at 14:56
  • You're right that while it has an equivalent result the `placeholder` syntax used here isn't an eta expansion. – Leo C Nov 08 '17 at 16:02
0

StringOps is a wrapper class of the String class which means that it "enriches" this class with additional methods like the one you are using.
When you want to call a method on a string instance, you simply do:

instanceName.methodName

This is exactly what you are doing in your first example.

However, in your second example you are doing the following:

methodName(instanceName)

toUpperCase does not take an argument, it is called on the instance of the string itself.

Joud C
  • 427
  • 3
  • 6