2015-02-11 11 views
8

Ich lief in eine seltsame Diskrepanz zwischen Funktionen und Objekten (scala 2.10):Implizite Konvertierungen für Defs/Lambdas in Scala?

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 

Warum gibt es einen Unterschied zwischen defs/Lambda-Literale und Lambda-vals?

Update: Offenbar tritt das Problem nicht für binäre Funktionen auftreten: Implicit conversion of a function to a second-order-function only works if the function to convert has at least two parameters

Ich habe diese, und in der Tat der folgende Code funktioniert:

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 

Ebenso Nullary Funktionen stellen kein Problem entweder:

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 

Also, die Frage neu zu formulieren: warum ist dies nicht für unäre Methoden und Funktionen arbeiten?

Update: Das Problem scheint auch auf den Zieltyp (der Typ von fs Argument h) bezogen zu sein. Die folgende funktioniert auch (diesmal zugunsten von „eta-Expansion zählt als hop“, weil wir eine Methode Wert aus einer mit _ erstellen müssen)

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 
+1

m aybe scalac betrachtet die Umwandlung von defs/lambda Literals in FunctionN bereits als impliziten Hop (von denen höchstens eins erlaubt ist)? –

+0

Überprüfen Sie nur die Spezifikationen. Eta-Erweiterung sollte kostenlos sein, so sollten die drei gleichwertig sein. View application: "Wir sagen, ein Typ T ist kompatibel zu einem Typ U, wenn T nach Anwendung von eta-expansion * und * view-Anwendungen schwach mit U übereinstimmt." –

+0

http://stackoverflow.com/questions/28456012/implicit-conversion-of-a-function-to-a-second-order-function-only-works-i-f-the-f –

Antwort

3

In scala defs sind methods und sind diffrent von 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> 

Sie können teilweise ihre Anwendung eine method-function konvertieren.

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

Also .. Sie tun können,

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, wie @ srgfed01 in seinem Kommentar erwähnt ... für den zweiten Fall ist das Problem, dass ... sie Typen nicht explizit angegeben werden Wenn Sie den Typ korrekt angeben, funktioniert der zweite Fall.

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

oder

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

Nun, über Unterschiede zwischen methods und functions ...

Sie können ein method keine Argumente ohne Klammer nehmen rufen () ... aber man kann ein nicht nennen Funktion ohne ().

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 

Einer der wichtigsten Gründe für methods unterscheidet sich von functions zu sein, ist, weil Schöpfer von Scala nahtlose Inter-Interoperabilität mit Java wollte, ohne die Java Begrenzungen stecken.

So methods (def) sind sehr ähnlich wie Java methods und functions unterscheidet sich von methods konnten sie mit grenzenlose Freiheit zu halten Scala zu schaffen, wie sie wollten.

Auch ...Ein anderer Hauptunterschied ist, dass methodsType-classes akzeptieren kann, wo functions nicht kann. Grundsätzlich können Sie generische methods wie

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 

Jetzt können Sie eine generische Methode definieren,

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

Aber man kann nicht eine Funktion wie folgt definieren,

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

oder wie diese

scala> (t: (T <: Behave)) => t.behave() 
<console>:1: error: ')' expected but '<:' found. 
    (t: (T <: A)) => t.behave() 
+0

Fehler können auch durch Definieren behoben werden Argumenttypen explizit: für den ersten Fall - 'f (a: Int => String)' und für den zweiten - 'f ((_. toString): Int => String)'. – user5102379

+0

Ich würde denken, "Fixieren der Methodentyp" hier ist eigentlich eine explizite Umwandlung in eine Funktion, so dass wir wieder bei der Frage, ob Eta-Erweiterung kommt kostenlos, wenn von View-Anwendung kommt, oder wenn es als implizite Konvertierung in kommt sein eigenes Recht. –

+0

Wo haben Sie "den Methodentyp reparieren" gesehen? –

Verwandte Themen