Reputation: 5849
I was reading this page, and I came across this function definition in scala:
def upper: String => String = _.toUpperCase
After trying it out, it seems that it works the same as this:
def upper = (str: String) => str.toUpperCase()
Are both functions the same? If so, how do I read the above function (first one)? does this way of defining definitions have a specific name?
Upvotes: 3
Views: 3569
Reputation: 149538
Are both functions the same?
Yes, both methods are the same. The latter is expanded by the compiler to match the former. This syntax is known as placeholder syntax for anonymous functions.
This definition is a method which yields a function. In Scala, methods and functions behave differently, as methods don't have a value (also called non-value types).
Upvotes: 3
Reputation: 31232
Both are methods returning a function object, on which the passed argument will be applied.
The first method returns a function object (shorthand for Function[A, B]
), so with explicit defn that it takes A(String) and emits B(String)
. (Static typing)
scala> def toUpperMethod: Function[String,String] = { case x => x.toUpperCase }
toUpperMethod: Function[String,String]
scala> toUpperMethod.apply("apply me") //.apply() is optional
res9: String = APPLY ME
scala> toUpperMethod("apply me")
res10: String = APPLY ME
While the second is anonymous method that returns a function object too which takes String
but can return Any
,
scala> def toUpperAnonymous = (x: String) => x.contains("hello") match { case true => x.toUpperCase case false => x.length }
upperFun: String => Any
scala> toUpperAnonymous.apply("hello") //.apply() is optional
res8: Any = HELLO
scala> toUpperAnonymous("yello")
res9: Any = 5
A traditional method returning a value(not a function object as in above examples) would be as below, which does not have a apply
function.
scala> def toUpperPureMethod(x: String) : String = x.toUpperCase
toUpperPureMethod: (x: String)String //has type (input)output
scala> toUpperPureMethod.apply("scala")
<console>:14: error: missing argument list for method toUpperPureMethod
Unapplied methods are only converted to functions when a function type is expected.
You can make this conversion explicit by writing `toUpperPureMethod _` or `toUpperPureMethod(_)` instead of `toUpperPureMethod`.
toUpperPureMethod.apply("scala")
But it can be wrapped to return as a function as below,
scala> def methodReturningFunction = toUpperPureMethod _
methodReturningFunction: String => String
scala> methodReturningFunction.apply("process me")
res11: String = PROCESS ME
input => output
is a function type.input(output)
denotes a methodUpvotes: 0
Reputation: 369468
I was reading […], and I came across this function definition in scala:
def upper: String => String = _.toUpperCase
That is not a function definition. That's a method definition, which is something very different.
After trying it out, it seems that it works the same as this:
def upper = (str: String) => str.toUpperCase()
Yes, that's exactly the same thing.
Are both functions the same?
Both methods are the same. (They aren't functions, they are methods.)
If so, how do I read the above function (first one)? does this way of defining definitions have a specific name?
It's not quite clear what you mean by "this way". Both are just normal method definitions. There are four differences between the two:
toUpperCase
is called without an argument list, in the second definition, toUpperCase
is called with an empty argument list. toUpperCase
is actually defined with an empty parameter list, so it should be called with an empty argument list; however, Scala allows calling a method that is defined with an empty parameter list without an argument list (but not the other way around, because of the obvious ambiguity). (Note that according to the community Scala style guide, toUpperCase
should be defined without a parameter list because it has no side-effects; however, in the original implementation of Scala, it is actually implemented as a Java method and Java methods must always have exactly one parameter list, it is impossible to define a Java method without a parameter list.)Note that this should rather be a val
than a def
, though: a val
is only evaluated once, when it is initialized, a def
is evaluated every time it is called. But this def
always returns the same thing anyway, so you could just as well make it a val
. (Or a lazy val
if you want to avoid constructing a function that may not be needed.)
I also noticed that you tagged your question with callbyname, but there is no call-by-name here. Everything is call-by-value in this example.
Here are a couple of other ways to write that same method:
def upper: Function1[String, String] = (str: String) => str.toUpperCase()
This is the most explicit one, all the others are just leaving stuff out from this one and leaving it to the compiler to figure out. But in all cases, the compiler will figure out this and the compiled code will be identical.
Option 1: use infix type constructor syntax for the generic return type of the method:
def upper: String Function1 String = (str: String) => str.toUpperCase()
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
Option 2: use the syntactic sugar for function types for the generic return type of the method:
def upper: String => String = (str: String) => str.toUpperCase()
// ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑
Option 3: leave out the explicit type of the function parameter:
def upper: Function1[String, String] = str => str.toUpperCase()
// ↑↑↑
Option 4: leave out the explicit return type of the method:
def upper = (str: String) => str.toUpperCase()
// ↑↑↑
Option 5: leave out the empty argument list of toUpperCase
:
def upper: Function1[String, String] = (str: String) => str.toUpperCase
// ↑↑
Option 6: use placeholder syntax for the function:
def upper: Function1[String, String] = _.toUpperCase()
// ↑
You can also combine several of these options, e.g. #3, #5, and #6:
def upper: String => String = _.toUpperCase
However! You need at least one type annotation, so that the compiler can either figure out the parameter type of the function from the return type of the method or figure out the return type of the method from the type of the function. Leaving out both won't work:
def upper = str => str.toUpperCase()
// <console>:11: error: missing parameter type
// def upper = str => str.toUpperCase()
// ^
def upper = _.toUpperCase()
// <console>:11: error: missing parameter type for expanded function ((x$1: <error>) => x$1.toUpperCase())
// def upper = _.toUpperCase()
// ^
(By the way, here you can also see how the compiler expands the placeholder syntax.)
Last but not least, you can use ⇒
instead of =>
both for the function type:
def upper: String ⇒ String = (str: String) => str.toUpperCase()
And the function literal:
def upper: Function1[String, String] = (str: String) ⇒ str.toUpperCase()
If I haven't miscounted, then this gives you a total of 44 different ways of expressing this same method:
def upper = (str: String) ⇒ str.toUpperCase
def upper = (str: String) ⇒ str.toUpperCase()
def upper = (str: String) => str.toUpperCase
def upper = (str: String) => str.toUpperCase()
def upper: Function1[String, String] = _.toUpperCase
def upper: Function1[String, String] = _.toUpperCase()
def upper: Function1[String, String] = (str: String) ⇒ str.toUpperCase
def upper: Function1[String, String] = (str: String) ⇒ str.toUpperCase()
def upper: Function1[String, String] = (str: String) => str.toUpperCase
def upper: Function1[String, String] = (str: String) => str.toUpperCase()
def upper: Function1[String, String] = str ⇒ str.toUpperCase
def upper: Function1[String, String] = str ⇒ str.toUpperCase()
def upper: Function1[String, String] = str => str.toUpperCase
def upper: Function1[String, String] = str => str.toUpperCase()
def upper: String ⇒ String = _.toUpperCase
def upper: String ⇒ String = _.toUpperCase()
def upper: String ⇒ String = (str: String) ⇒ str.toUpperCase
def upper: String ⇒ String = (str: String) ⇒ str.toUpperCase()
def upper: String ⇒ String = (str: String) => str.toUpperCase
def upper: String ⇒ String = (str: String) => str.toUpperCase()
def upper: String ⇒ String = str ⇒ str.toUpperCase
def upper: String ⇒ String = str ⇒ str.toUpperCase()
def upper: String ⇒ String = str => str.toUpperCase
def upper: String ⇒ String = str => str.toUpperCase()
def upper: String => String = _.toUpperCase
def upper: String => String = _.toUpperCase()
def upper: String => String = (str: String) ⇒ str.toUpperCase
def upper: String => String = (str: String) ⇒ str.toUpperCase()
def upper: String => String = (str: String) => str.toUpperCase
def upper: String => String = (str: String) => str.toUpperCase()
def upper: String => String = str ⇒ str.toUpperCase
def upper: String => String = str ⇒ str.toUpperCase()
def upper: String => String = str => str.toUpperCase
def upper: String => String = str => str.toUpperCase()
def upper: String Function1 String = _.toUpperCase
def upper: String Function1 String = _.toUpperCase()
def upper: String Function1 String = (str: String) ⇒ str.toUpperCase
def upper: String Function1 String = (str: String) ⇒ str.toUpperCase()
def upper: String Function1 String = (str: String) => str.toUpperCase
def upper: String Function1 String = (str: String) => str.toUpperCase()
def upper: String Function1 String = str ⇒ str.toUpperCase
def upper: String Function1 String = str ⇒ str.toUpperCase()
def upper: String Function1 String = str => str.toUpperCase
def upper: String Function1 String = str => str.toUpperCase()
Upvotes: 4
Reputation: 1594
This is actually not a function but a method. You can read about the difference between the two here and here
Both of this are the same it's a method called upper with no parameters that returns a function from String to String.
def upper: String => String = _.toUpperCase
String => String
is the return type of upper. what comes after the equal sign is the implementation. The underscore is just a syntactic sugar used to refer the function parameter.
Upvotes: 6