4

I've just started Scala and am tinkering in worksheets. For example:

def merp(str: String) : String = s"Merrrrrrrp $str"
val merp2 = (str: String) => s"Merrrrrrrp $str"
val merp3 = (str: String) => merp(str)
val merp4 = merp _
merp("rjkghleghe")
merp4("rjkghleghe")

And the corresponding worksheet results:

merp: merp[](val str: String) => String
merp2: String => String = <function1>
merp3: String => String = <function1>
merp4: String => String = <function1>
res0: String = Merrrrrrrp rjkghleghe
res1: String = Merrrrrrrp rjkghleghe

Saying, for example, val merp5 = merp produces an error, because apparently methods cannot be values the way functions can. But I can still pass methods as arguments. I demonstrate this in the following code snippet, adapted from a similar SO question:

def intCombiner(a: Int, b: Int) : String = s"herrrrrrp $a derrrrrrp $b"
def etaAbstractor[A, B](combineFoo: (A, B) ⇒ String, a: A, b: B) = combineFoo(a, b)
etaAbstractor(intCombiner, 15, 16)

worksheet result:

intCombiner: intCombiner[](val a: Int,val b: Int) => String
etaAbstractor: etaAbstractor[A,B](val combineFoo: (A, B) => String,val a: A,val b: B) => String
res10: String = herrrrrrp 15 derrrrrrp 16

  1. Is methods-not-being-first-class a limitation, perhaps imposed by Scala's JVM interaction, or is it a decision in the language's design?
  2. Why do I need to roll my own eta abstractions, as in merp3?
  3. Is merp4 also an eta abstraction, or is it something sneakily similar?
  4. Why does my etaAbstractor work? Is Scala quietly replacing intCombiner with intCombiner _?

Theoretical, computer sciencey answers are welcome, as are pointers to any relevant points in the language specification. Thanks!

Community
  • 1
  • 1
Nobbynob Littlun
  • 381
  • 1
  • 11
  • The design and implementation of Scala *is* closely coupled to Java/JVM compatibility in many areas - I'm fairly certain it would be a thinner difference without such, but you'd have to ask Martin (I think he visits SO on occasion) or find some earlier impl notes. Although they can be easily 'promoted' with `method _`. – user2864740 May 28 '15 at 01:40

2 Answers2

3

Disclaimer: I'm not a computer scientist, but I will try to guess:

  1. Method is a part of an object and doesn't exist outside of it. You can't pass method alone. Closure is another (equivalent?) way of encapsulating state, by converting an object method to a standalone function (which is by the way just another object with apply() method in Scala) you are creating a closure. This process is known as eta-expansion. §3.3.1, §6.26.5

  2. You don't have to. You can also write val merp3 : (String => String) = merp. §6.26.5

  3. Yes, merp4 is eta-expansion too. §6.7

  4. §6.26.2

kiritsuku
  • 52,967
  • 18
  • 114
  • 136
Victor Moroz
  • 9,167
  • 1
  • 19
  • 23
  • 1
    Python and JavaScript have methods that *are* first-class functions so #1 is "In Scala (and Java) ..". – user2864740 May 28 '15 at 02:21
  • 1
    @user2864740 In Python unbound method can't be called, so it doesn't look to me as a first-class function. Assigning a bound method looks like eta-expansion under the hood. Javascript has the same problem if you use `this` inside your function, only instead of error you will have `undefined` as a value of `this`. – Victor Moroz May 28 '15 at 11:29
  • It still remains that methods are *not* first-class values but are rather 'manifestations of definitions' that are invoked-by-name (or [message passing OO](http://www.desy.de/gna/html/cc/Tutorial/node5.htm#SECTION00540000000000000000)). Maintaining a context (or not) doesn't affect their citizenship status: or the fundamental implementation differences. – user2864740 May 28 '15 at 18:15
  • Many thanks for the links to specs. To complete Q#1, I speculate that it is a language design decision resulting from how the Scala code gets translated into the JVM. Specifically, a method (being an abstraction with no value) can be finalized on compilation, while a `Function` -- being an object, and its attendant closure being a dynamic tree structure -- can only be created at runtime. To make the method concrete, a `Function` object must be created. This can have performance consequences, so it'd be a good decision to require that programmers explicitly create that object and not mislead. – Nobbynob Littlun Jun 06 '15 at 02:01
2

The reason it works with etaAbstractor is that the compiler can infer that a function (not a function invocation) is required.

If I had to guess why the underscore is required where a function type cannot be inferred, I'd think that it's to improve error reporting of a common class of errors (getting functions where invocations are intended). But again, that's just a guess.

In the JVM, a method is not an object, whereas a first-class function must be one. So the method must be boxed into an object to convert it to a function.

Ed Staub
  • 15,480
  • 3
  • 61
  • 91