2015-09-26 9 views
8

Jest kilka dyskusji na ten temat, ale mam kilka konkretnych pytań, na które nie mogłem znaleźć odpowiedzi. Więc przez call-by-name, mam na myśli =>T, i przez funkcję 0-arity mam na myśli () => TKilka pytań na temat różnicy między nazwiskami i funkcjami 0-arity

Rozumiem (myślę) różnicę pojęciową, ale prawdopodobnie brakuje mi czegoś, ponieważ wciąż mam wiele pytań :

  1. Dlaczego w ogóle mamy koncepcję =>T, jeśli zawsze możemy użyć () => T?
  2. Czy są jakieś ograniczenia składni/funkcjonalności każdego z nich? Na razie znalazłem tylko, że => nie można używać jako pola klasy. Czy to jedyne ograniczenie?
  3. Czy wygenerowany kod jest zawsze taki sam dla obu?
  4. Czy zawsze powinienem preferować =>T? I dlaczego?
  5. Czy prawidłowe jest wywoływanie =>T typu? Wygląda na to, że nie ma żadnej reprezentacji typu w scala.
+0

Rozważmy następujący komentarz - https : //twitter.com/StewOConnor/status/633555338894020608 –

Odpowiedz

7

1) To jest po prostu bardziej poręczny go używać, zwłaszcza wewnątrz DSL:

def printAndGet[T](f: => T) = { 
    val res = f 
    println(res + " printed") 
    res 
} 

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

val k = printAndGet { 
    val a = 5 
    5 * a 
} 

// Exiting paste mode, now interpreting. 

25 printed 
k: Int = 25 

2) => T może być tylko parametr metody lub funkcji. I rzeczywiście => T i () => T nie są wymienne:

scala> def aaa(f: => String) = f 
aaa: (f: => String)String 

scala> val a: Function1[() => String, String] = aaa _ 
<console>:8: error: type mismatch; 
found : (=> String) => String 
required: (() => String) => String 
     val a: Function1[() => String, String] = aaa _ 
               ^

Dzięki @ som-snytt, fоund ten:

scala> object O { def f(i: Int) = i; def f(i: => Int) = i + 1 } 
defined object O 

scala> O.f(5) 
res12: Int = 5 

scala> O.f(5: (=> Int)) 
<console>:1: error: no by-name parameter type allowed here 
     O.f(5: (=> Int)) 
     ^

Nawet ten, który powinien działać, jeśli kompiluje - ale tak nie jest (scala 2.11.2, 2.11.5 REPL tylko wywala):

scala> val k: (=> Int) => Int = O.f _ 
k: (=> Int) => Int = <function1> 

scala> k(5) //should be 6 
res18: Int = 5 //WTF? 

Ostatnio jeden wydaje się błędem

3) Nie do końca, jeśli chcesz to samo, tylko konwertować => T w () => T:

scala> def aaa(f: => String) = {f _} 
aaa: (f: => String)() => String 

Bytecode mogą także różnić. Na przykład, kompilator będzie bardziej prawdopodobnie wbudowany w kod od => T bez generowania dla niego lambda. Tak więc kluczową różnicą jest to, że () => T jest w rzeczywistości obiektem (obywatelem pierwszej klasy), nie jest to => T.

4) patrz 1, ale czasami może zajść potrzeba upewnienia się, że użytkownik wie, że obliczenia mogą być opóźnione - () => T jest wtedy lepsza.

5) To część podpisu typu, wystarczy spojrzeć na eta-ekspansji:

scala> def aaa(f: => String) = {f} 
aaa: (f: => String)String 

scala> aaa _ //convert method into a function 
res7: (=> String) => String = <function1> 

scala> val a: (=> String) => String = aaa _ 
a: (=> String) => String = <function1> 

Jednak scala nie rozpoznaje go jako niezależny typ:

scala> val a: Function1[(=> String), String] = aaa _ 
<console>:1: error: no by-name parameter type allowed here 
     val a: Function1[(=> String), String] = aaa _ 
         ^
+0

W # 2, nie można używać asocjacji typu-nazwy, więc w przypadku przeciążonych 'f (i: Int)' i 'f (i: => Int)' , nie możesz 'f (0: => Int)'.Podobny do twojego punktu o "niezależnym" typie. –

+0

1) Rozumiem to. Nie rozumiem, dlaczego potrzebujemy specjalnej notacji '=> T'? Zawsze możemy użyć '() => T' i założyć, że każde wyrażenie ma postać'() => ', a nie' => T'. – Archeg

+0

2) Nie wydaje się to prawdą. Mogę przynajmniej użyć go jako lokalnego val: 'def aa (f: => String) = {val g = f; println (g)} 'then aa {println (" przetworzony "); "return val"} '. Zastanawiam się, jakie inne użycie może być – Archeg

Powiązane problemy