2013-10-20 2 views
22

Dеar Scala,In che modo una funzione anonima "caso" funziona davvero in Scala?

scala> val f1: ((Int, Int)) => Int = { case (a, b) => a + b } 
f1: ((Int, Int)) => Int = <function1> 

scala> val f2: (Int, Int) => Int = { case (a, b) => a + b } 
f2: (Int, Int) => Int = <function2> 

eh ?!

scala> f1(1, 2) 
res2: Int = 3 

Ok ...

scala> def takesIntInt2Int(fun: (Int, Int) => Int) = fun(100, 200) 
takesIntInt2Int: (fun: (Int, Int) => Int)Int 

scala> def takesTuple2Int(fun: ((Int, Int)) => Int) = fun(100, 200) 
takesTuple2Int: (fun: ((Int, Int)) => Int)Int 

scala> takesIntInt2Int(f2) 
res4: Int = 300 

scala> takesIntInt2Int(f1) 
<console>:10: error: type mismatch; 
found : ((Int, Int)) => Int 
required: (Int, Int) => Int 
       takesIntInt2Int(f1) 
          ^

scala> takesTuple2Int(f1) 
res6: Int = 300 

scala> takesTuple2Int(f2) 
<console>:10: error: type mismatch; 
found : (Int, Int) => Int 
required: ((Int, Int)) => Int 
       takesTuple2Int(f2) 

destro. E ora, guarda questo!

scala> takesTuple2Int { case (a, b, c) => a + b + c } 
<console>:9: error: constructor cannot be instantiated to expected type; 
found : (T1, T2, T3) 
required: (Int, Int) 
       takesTuple2Int { case (a, b, c) => a + b + c } 
            ^

scala> takesIntInt2Int { case (a, b, c) => a + b + c } 
<console>:9: error: constructor cannot be instantiated to expected type; 
found : (T1, T2, T3) 
required: (Int, Int) 
       takesIntInt2Int { case (a, b, c) => a + b + c } 

Come, srsly? o_O Entrambi risultano nell'errore required: (Int, Int).

Perché utilizzare quindi case in tali funzioni anonime?

risposta

15

Vedere la sezione 8.5 del riferimento Scala (http://www.scala-lang.org/files/archive/nightly/pdfs/ScalaReference.pdf). L'espressione { case (a, b) => a + b } viene interpretata in modo diverso in base al tipo previsto. Nella tua definizione di f1 ha creato un PartialFunction[(Int, Int), Int] che è stato assegnato a un Function1[(Int, Int), Int], ad esempio ((Int, Int)) => Int mentre nella definizione di f2 ha creato un Function2[Int, Int, Int], ad esempio (Int, Int) => Int.

Queste due interpretazioni riguardano le due situazioni in cui si utilizza comunemente il caso in una funzione anonima.

Uno è per scrivere funzioni anonime che accettano tuple e funzionano sui loro componenti, come hai fatto con f1. Un esempio potrebbe essere la funzione che si passa al metodo foreach o map su un Map, ad es. Map(1 -> 2, 3 -> 4) map { case (k, v) => k + v }.

Il secondo è per la scrittura di una funzione anonima che fa un match sul suo unico parametro. Il tuo f2 sta facendo questo, ma non in alcun modo utile. Un esempio potrebbe essere la funzione anonima passata a collect, ad es. List(1, -2, 3) collect { case x if x > 0 => -x }.

Si noti che i due possono essere combinati, ovvero funzioni come f1 possono anche eseguire corrispondenze complesse. Ad esempio, Map(1 -> 2, 3 -> 4) collect { case (k, v) if k < 2 => v }.

Modifica: res2 funziona a causa della tupling. Se un'applicazione non digita il controllo, il compilatore proverà a racchiudere gli argomenti in una tupla prima di fallire.

Ma questo viene provato solo per le applicazioni; non è una conversione generale, come hai scoperto. Non tenterà di aggiornare un valore Function2[A, B, C] a Function1[(A, B), C].

+1

Sarebbe bello poter fare tutto questo senza usare la parola chiave 'case'. Perché la differenza nella sintassi per Function e PartialFunction, dal punto di vista dello sviluppatore normale? –

+2

@ MichałRus Ad essere sincero, questo mi ha sempre infastidito un po '. Haskell e Clojure hanno sintassi molto più semplici per esprimere la corrispondenza del modello direttamente sui parametri di una funzione. – wingedsubmariner