2011-10-07 16 views
6

W poniższym przykładzie kodu, nie rozumiem, dlaczego funkcja fun może być przekazana jako argument do metody addAction. Sposób jest następujący: , a sposób według wynalazku wymaga funkcji typu .Metody i metody metod Scala jako parametry

Jeśli fun jest typu () => Unit, to dlaczego kompilator skarżą się, że fun jest typu Unit, gdy próbuję dodać fun do listy czynności: actions = fun :: actions?

package myscala 

object MyScala { 

    def fun() { println("fun1 executed.") } 

    def addAction(a:() => Unit) { 
    actions = a :: actions 
    } 

    var actions: List[() => Unit] = List() 

    def main(args: Array[String]) { 
    // the following line would produce a compiler error (found: Unit, required:() => Unit), it's OK 
    // actions = fun :: actions 
    actions = (() => fun) :: actions // OK 
    // I would expect the same compiler error here (found: Unit, required:() => Unit), but it's OK why? 
    addAction(fun) 
    actions.foreach(_()) // prints twice "fun1 executed" 
    } 
} 

Odpowiedz

8

Weź to jako przykład wprowadzający:

def fun() { println("fun1 executed.") } 

val a1 = fun 
val a2:() => Unit = fun 

Obie linie kompilacji i (dzięki wpisać wnioskowanie) wyglądają odpowiednik. Jednak a1 jest typu Unit, a a2 jest typu () => Unit ... Jak to jest możliwe?

Ponieważ nie są wyraźnie zapewniając rodzaj a1, kompilatory interpretuje fun jako metoda fun połączenia typu Unit, stąd rodzaj a1 jest taki sam jak typ fun. Oznacza to również, że ta linia wydrukuje fun1.

Jednak a2 jawnie zadeklarował typ () => Unit. Kompilator pomaga tutaj i rozumie, że ponieważ kontekst wymaga funkcji typu () => Unit i podałeś metodę pasującą do tego typu, nie powinna wywoływać tej metody, ale traktować ją jako funkcję pierwszej klasy!

Nie jesteś skazany na określenie typu a1 jawnie. Mówiąc:

val a1 = fun _ 

Czy teraz rozumiesz, gdzie jest twój problem?

+0

Tak, robię. Teraz wydaje mi się to oczywiste (w ogóle wtedy nie było). Kiedy kompilator jest w stanie wywnioskować, że oczekiwany jest typ funkcji, mogę po prostu napisać "fun", w przeciwnym razie muszę wyraźnie zaznaczyć, że przekazuję funkcję. Dzięki za jasne odpowiedzi dla was wszystkich! – Manu

+0

@Manu: Rozważ zaakceptowanie odpowiedzi, którą uważasz za najlepszą (niekoniecznie tę) –

5

Musisz napisać fun _ w pierwszym przypadku, aby uniknąć wywołania metody i wykonywania ETA-ekspansję zamiast.

to będzie działać:

actions = (fun _) :: actions 

Jeśli tego nie zrobi, to fun jest oceniany.

Aby uzyskać więcej informacji, patrz sekcja 6.7 (Wartości metod) z Scala Language Reference.

Co do tego, dlaczego fun nie jest oceniane w drugim przypadku, to dlatego, że wnioskowanie o typie może jednoznacznie stwierdzić, że addAction oczekuje funkcji. Nawiasem mówiąc, typ fun jest technicznie ()Unit, a nie Unit, czyli typem metody, a nie typem wartości. Więcej informacji można znaleźć w rozdziale 3.3.1 w reference.

+3

wolałbym polecam lżejszą lekturę: [Programowanie w Scala Rozdział 9] (http://www.artima.com/pins1ed/control-abstraction.html) – Jamil

3

Istnieje różnica między metodami i funkcjami. W twoim przypadku actions jest listą funkcji.Gdy kompilator wie, że funkcja jest wymagana (jak w przypadku addAction), może automatycznie przekształcić metodę fun w funkcję. Teraz metoda :: jest również metodą, dlatego też kompilator wie, że przyjmuje funkcje jako parametry. Ale problemem jest syntaktyczny cukier prawostronnego operatora ::. Gdybyś nazwał to jak metodą: actions.::(fun) to się skompiluje (chociaż nie mogę tego przetestować w tej chwili). Podczas pisania fun :: actions kompilator myśli, że fun jest wyrażeniem, a zatem ocenia je, a ponieważ "zwraca" Unit otrzymujesz błąd kompilatora.

EDIT

Ponieważ teraz mają możliwość przetestowania moja hipoteza (co było źle) są tu opcje:

// Usual syntax 
actions.::[() => Unit](fun) 
actions.::(fun:() => Unit) 
actions.::(fun _) 
// Operator syntax 
(fun:() => Unit) :: actions 
(fun _) :: actions 
+0

'actions.: :(fun)' zwraca wartość do 'List [Any] = List (())'. Wciąż jednak ocenia funkcję. Zastanawiam się, dlaczego Lista [() => Jednostka] jest konwertowana na Listę [Any]? współzmienność? – Jamil

+2

Tak, 'Lista [Any]' jest najbardziej precyzyjnym typem dla listy, która może zawierać 'Unit's i'() => Unit's. "Jednostka", widziana jako '()' jest tym, co zwraca 'zabawa'. – Philippe

+0

Edytowałem swoją odpowiedź. – agilesteel

Powiązane problemy