Żeby sobie to wszystko w głowie poukładać trzeba wziąć pod uwagę kilka rzeczy.
- Funkcja, a metoda to co innego. Funkcja jest instancją typu. Konkretnie to typ
Int => Int
jest faktycznie typem Function1[Int, Int]
. Widać to nawet w przypadku implementowania funkcji explicit, np IntelliJ podpowiada, by zamienić coś takiego class Xxx extends Function1[Int, Int]
na class Xxx extends (Int => Int)
. Metodę można podnieć (lift) do funkcji za pomocą np operatora _
, czyli
def method(a: Int): String = ???
val function = method _
Często operator _
jest niepotrzebny, jeśli wstawiamy nazwę metody jako parametr, który ma być funkcją, np:
def hof(function: Int => Int): Unit = ???
def method(a: Int): Int = ???
hof(method) // <- tutaj nie musimy wstawiać _
Operator _
może również posłużyć do zamiany call-by-name na Function0 tak jak tutaj:
def m1(x: => Int): Int = m2(x _)
def m2(x: () => Int): Int = x()
- Call by name to w zasadzie cukier składniowy na Function0 czyli na funkcje typu
() => R
gdzie R jest typem zwracanym. W bajtkodzie Javowym w przypadku stosowania call-by-name kompilator wprost tłumaczy to na Function0. Jednak składniowo są to różne byty i trzeba między nimi ręcznie konwertować. W zapisie call-by-name od Function0 różni się tym, że call-by-name nie wymaga nawiasów. Mamy więc równoważne zapisy:
def m() = {println("m"); 0}
def callByName(x: => Int) = {println("call by name"); x}
callByName(m())
def m() = {println("m"); 0}
def callFunction0(x: () => Int) = {println("call function0"); x()}
callFunction0(() => m())
- Funkcje bezparametrowe są czasami trikowe w zrozumieniu, bo kompilator dodaje sobie niejawne nawiasy jeśli ich brakuje. Np w poniższym kodzie:
def m(): Int = 5
val x = m // nie trzeba nawiasów, kompilator sobie sam dopisuje
println(x)
Ten ficzer jest średnio pomocny i chyba ma zostać zredukowany w kolejnych wersjach Scali.