2011-01-13 15 views
71

Wiele list parametrów, np. def foo(a:Int)(b:Int) = {} i wiele parametrów na liście, np. def foo(a:Int, b:Int) = {} są semantycznie równoważne, o ile mogę powiedzieć, a większość języków funkcjonalnych ma tylko jeden sposób deklarowania wielu parametrów, np. FA#.Dlaczego Scala udostępnia zarówno listę wielu parametrów, jak i wiele parametrów na liście?

Jedynym powodem, dla którego mogę wymyślić obsługę obu tych stylów definicji funkcji, jest umożliwienie rozszerzenia języka podobnego do składni przy użyciu listy parametrów, która zawiera tylko jeden parametr.

def withBufferedWriter(file: File)(block: BufferedWriter => Unit) 

można teraz nazywa się składnia wyglądające

withBufferedWriter(new File("myfile.txt")) { out => 
    out write "whatever" 
    ... 
} 

Jednak mogą istnieć inne sposoby wspierania użycia nawiasów klamrowych, bez konieczności wiele list parametrów.

Pokrewne pytanie: dlaczego w Scali używa się wielu list parametrów, zwanych "currying"? Currying jest zwykle definiowany jako technika, która sprawia, że ​​funkcja n-ary staje się unarna ze względu na poparcie częściowego zastosowania. Jednak w Scali można częściowo zastosować funkcję bez tworzenia "curry" (lista wielu parametrów z jedną wersją) w funkcji.

+0

ogólnie: to po prostu bardziej matematycznie wyraziste.Dlaczego wystarczy z jedną listą, kiedy można uogólniać do wielu :) – matanster

Odpowiedz

75

To sprawia, że ​​jesteś w stanie to zrobić np

scala> def foo(as: Int*)(bs: Int*)(cs: Int*) = as.sum * bs.sum * cs.sum 
foo: (as: Int*)(bs: Int*)(cs: Int*)Int 

scala> foo(1, 2, 3)(4, 5, 6, 7, 9)(10, 11) 
res7: Int = 3906 
+2

Jednym z ograniczeń tego jest to, że chociaż masz zmienną długość dla każdej grupy parametrów, liczba grup musi być ustalona (w tym przypadku na 3). –

+25

Ponieważ zaakceptowałeś tę odpowiedź, należy zauważyć, że jest to tylko przykład i może nie być kanoniczną odpowiedzią na pytanie "dlaczego". –

+1

Jest to pierwszy konkretny powód, dla którego dotychczas widziałem oba rodzaje parametrów. Wszystkie pozostałe są dość ad hoc. Mimo to, nadal nie widzę żadnego z tych powodów, dla których warto składać bagaż syntaktyczny i komplikować język. –

23

Aby odpowiedzieć na to „Related pytanie” Zmiękczanie jest po prostu sposobem na włączanie funkcji wielu argumentów, na przykład (A, B, C) => D, do funkcji, która przyjmuje jedną argument i zwraca funkcję, np A => (B => (C => D)) (nawiasy są wyświetlane, ale nie są konieczne).

Krotki formularz i forma curry są izomorficzne i możemy swobodnie tłumaczyć między nimi. Wszystkie te są równoważne, ale mają różne konsekwencje składniowe:

(A, B, C, D, E) => F 
((A, B), (C, D, E)) => F 
(A, B) => (C, D, E) => F 

Kiedy deklarujesz oddzielne grupy parametrów, jest to rodzaj currying robisz. Metoda Wieloparametrowa grupa to metoda, która zwraca funkcję ... widać to w REPL:

scala> def foo(a:Int, b:Int)(c:Int, d:Int, e:Int):Int = 9 
foo: (a: Int,b: Int)(c: Int,d: Int,e: Int)Int 

scala> foo _            
res4: (Int, Int) => (Int, Int, Int) => Int = <function2> 
43

jak również pozwala na pisanie metod, które wyglądają jak część języka (które już zauważony), warto zauważyć, że inferencer typów będzie działał z jednym blokiem na raz.

więc w tym:

def foo[T](a: T, b: T)(op: (T,T)=>T) = op(a,b) 
foo(1,2){_+_} 

T najpierw wywnioskować jak Int, które następnie zostaną wykorzystane jako typ dwoma podkreśleniami w zamknięciu. W ten sposób kompilator wie, z pełnym typem bezpieczeństwa, że ​​operacja + jest ważna.

12

Wiem, że jedną z motywacji były niejawne listy parametrów. "niejawny" jest właściwością listy, a nie parametrem. Innym prawdopodobnie były klasy case: tylko pierwsza lista parametrów staje się polami case.

+0

Interesujące. Czy jest szansa, że ​​możesz to rozwinąć? –

+1

Wygląda na to, że nie ma szansy ... – zapadlo

+0

W przypadku klas przypadków z wieloma listami parametrów zobacz [komentarze do tego błędu] (https://issues.scala-lang.org/browse/SI-5009). Właśnie tam najpierw usłyszałem o tej funkcji. – ebruchez

18

Powrót odniesienia w domyślnych argumentów:

case class Foo(bar: Int) 

def test(f: Foo)(i: Int = f.bar) = i*i 

test(Foo(3))() 
Powiązane problemy