rgamber
rgamber

Reputation: 5849

Reading a scala function

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

Answers (4)

Yuval Itzchakov
Yuval Itzchakov

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

prayagupadhyay
prayagupadhyay

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

NOTE

  • anything of type input => output is a function type.
  • anything of type input(output) denotes a method

Upvotes: 0

J&#246;rg W Mittag
J&#246;rg W Mittag

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:

  • In the first definition, the return type of the method is explicitly annotated, in the second definition, it is left to the compiler to be inferred. The name for the first feature is "type annotation" or "explicit typing", the name for the second feature is "type inference" or "implicit typing".
  • The first definition uses placeholder syntax to construct the function to be returned from the method, the second definition uses a function literal.
  • In the second definition, the parameter for the anonymous function is explicitly annotated with a type, in the first definition, it isn't even mentioned, since we use placeholder syntax.
  • In the first definition, 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 , 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

NetanelRabinowitz
NetanelRabinowitz

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

Related Questions