8

I just ran into a strange disparity between functions and objects (scala 2.10):

implicit def conv(c: Int => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)

def a(x: Int) = x.toString
val b = (x: Int) => x.toString

//    def main(args: Array[String]) = f(a) // fail
//    def main(args: Array[String]) = f((x: Int) => x.toString) // fail
def main(args: Array[String]) = f(b) // ok

Why is there a difference between defs/lambda literals and lambda vals?

Update: apparently, the Problem does not occur for binary functions: Implicit conversion of a function to a second-order-function only works if the function to convert has at least two parameters

I checked this, and indeed the following code works:

implicit def conv(c: (Int,Unit) => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v,()))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)

def a(x: Int, y : Unit) = x.toString
val b = (x: Int, y : Unit) => x.toString

def main(args: Array[String]) = f(a) // ok
def main(args: Array[String]) = f((x: Int, y: Unit) => x.toString) // ok
def main(args: Array[String]) = f(b) // ok

Likewise, Nullary functions don't pose a problem, either:

implicit def conv(c: () => String) : (PrintStream => Int => Unit) = p => v => p.println(c())
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)

def a() = "1"
val b = () => "1"

def main(args: Array[String]) = f(a) // ok
def main(args: Array[String]) = f(() => "1") // ok
def main(args: Array[String]) = f(b) // ok

So, rephrasing the question: why does this not work for UNARY methods and functions?

Update: the problem also seems to be related to the target type (the type of f's argument h). The following also works (this time, in favour of "eta-expansion counts as hop", because we need to create a method value from a using _)

implicit def conv(c: Int => String) : Unit = ()
def f(h: Unit) : Unit = System.out.print("?")

def a(x: Int) = x.toString
val b = (x: Int) => x.toString

def main(args: Array[String]) = f(a _) // ok
def main(args: Array[String]) = f((x: Int) => x.toString) // ok
def main(args: Array[String]) = f(b) // ok
Community
  • 1
  • 1
  • 1
    maybe scalac considers conversion of defs/lambda literals to FunctionN already an implicit hop (of which at most one is allowed)? – Björn Karge Feb 11 '15 at 13:02
  • Just checked the specs. Eta-expansion should come for free , so the three should be equivalent w.r.t. view application: "We say, a type T is compatible to a type U if T weakly conforms to U after applying eta-expansion *and* view applications." – Björn Karge Feb 11 '15 at 16:07
  • http://stackoverflow.com/questions/28456012/implicit-conversion-of-a-function-to-a-second-order-function-only-works-if-the-f – Björn Karge Feb 12 '15 at 02:21

1 Answers1

3

In scala defs are methods and are diffrent from functions.

scala> def a( x: Int, y: Int ): Int = x + y
a: (x: Int, y:Int)Int

scala> (x: Int, y: Int) => x + y
res0: (Int, Int) => Int = <function2>

You can convert a method to function by partially applying it.

scala> b _
res1: (Int, Int) => Int = <function2>

So.. you can do,

implicit def conv(c: Int => String) : (PrintStream => Int => Unit) = p => v => p.println(c(v))
def f(h: PrintStream => Int => Unit) : Unit = h(System.out)(1)

def a(x: Int) = x.toString
val af = a _

def main( args: Array[ String ] ) = f( af )

Alse, as @srgfed01 mentioned in his comment... for the second case the problem is that... they types are not explicitly specified, if you specify the type correctly... the second case will work.

scala> f( ( a => a.toString ): (Int => String) )
1

or

scala> f( ( _.toString ): (Int => String) )
1

Now, about differences between methods and functions...

You can call a method taking no arguments without parenthesis ()... but you can not call a function without ().

scala> def g() = 5
g: ()Int

scala> g
res15: Int = 5

scala> () => 5
res13: () => Int = <function0>

scala> res13
res14: () => Int = <function0>

scala> res13()
res15: 5

One of the most important reasons for methods being different from functions is because creators of Scala wanted seamless inter-interoperability with Java without being stuck with Java's limitations.

So methods (def) are very much similar to Java methods and keeping functions different from methods enabled them with limitless freedom to create Scala, the way they wanted.

Also... Another major difference is that methods can accept Type-classes where as functions can not. Basically you can have generic methods like

scala> :paste

trait Behave {
    def behave
}

class A( elem: String ) extends Behave {
  def behave() {
    println( elem )
  }
}

// Exiting paste mode, now interpreting.

defined trait Behave
defined class A

Now you can define a generic method,

scala> def check[ T <: Behave ]( t: T ): Unit = t.behave()
check: [T <: Behave](t: T)Unit

But you can not define a function like this,

scala> ( t: T ) => t.behave()
<console>:8: error: not found: type T
          ( t: T ) => t.behave()

or like this

scala> ( t: (T <: Behave) ) => t.behave()
<console>:1: error: ')' expected but '<:' found.
   ( t: (T <: A) ) => t.behave()
sarveshseri
  • 13,738
  • 28
  • 47
  • Errors can also be fixed by defining argument types explicitly: for first case - `f(a: Int => String)` and for second - `f((_.toString): Int => String)`. – user5102379 Feb 11 '15 at 13:41
  • I would think "fixing the method type" here is actually an explicit conversion to a function, so we'd be back at the question if eta-expansion comes for free when followed by view application, or if it counts as implicit conversion in its own right. – Björn Karge Feb 11 '15 at 16:21
  • Where did you see "fixing the method type" ? – sarveshseri Feb 11 '15 at 16:33
  • I was referring to @srgfed01's comment, sorry. – Björn Karge Feb 11 '15 at 23:14
  • @Sarvesh: call me slow, but I don't see how your (very nice) function/method primer answers the question. – Björn Karge Feb 12 '15 at 02:42
  • I guess... Your question had two parts - 1) Why your two cases don't work... and how to fix them. 2) Why defs and functions were made to behave differently in Scala. If you take the time to read the answer... it explains both. – sarveshseri Feb 12 '15 at 03:21
  • Also... Scala has `functions` as `first class members` and these are fundamentally different and more powerful than `lambdas` ( as generally conceptualized in other languages..). So... lets call them just `anonymous functions` – sarveshseri Feb 12 '15 at 03:24
  • As the problem only occurs for unary methods/functions, I doubt that the phenomenon can be explained by referring to the method/function dichotomy. – Björn Karge Feb 12 '15 at 05:24