2011-11-24 11 views
6

Funkcja w Scali jest obiektem, który implementuje jedną z cech FunctionN. Na przykład:Co to jest funkcja FunctionN, która reprezentuje funkcję przyjmującą parametr nazwy?

scala> def f(x: Int) = x * x 
f: (x: Int)Int 

scala> val ff = f _ 
ff: Int => Int = <function1> 

scala> val fff: Function1[Int, Int] = f _ 
fff: Int => Int = <function1> 

Jak dotąd, tak dobrze. Ale co, jeśli mamy funkcję, która przyjmuje parametr nazwy? To na pewno ma jeszcze wdrożyć jedną z FunctionN cech:

scala> def g(x: => Int) = x * x 
g: (x: => Int)Int 

scala> val gg = g _ 
gg: => Int => Int = <function1> 

scala> gg.isInstanceOf[Function1[_, _]] 
res0: Boolean = true 

Ale jakiego rodzaju to jest dokładnie? Nie Function1[Int, Int] to:

scala> val ggg: Function1[Int, Int] = g _ 
<console>:8: error: type mismatch; 
found : => Int => Int 
required: Int => Int 
     val ggg: Function1[Int, Int] = g _ 
            ^

Nie jest to Function1[Function0[Int], Int]:

scala> val ggg: Function1[Function0[Int], Int] = g _ 
<console>:8: error: type mismatch; 
found : => Int => Int 
required:() => Int => Int 
     val ggg: Function1[Function0[Int], Int] = g _ 
               ^

I Function1[=> Int, Int] nie kompilacji:

scala> val ggg: Function1[=> Int, Int] = g _ 
<console>:1: error: identifier expected but '=>' found. 
     val ggg: Function1[=> Int, Int] = g _ 
         ^

Więc o co chodzi?

Odpowiedz

4

Nazwa jest bardzo przydatna, ale niebezpieczna poza systemem typu em

Parametry przydomowe Scala są cukrem syntaktycznym, aby kod był bardziej czytelny, gdy wymagana jest leniwość. Bez tego musielibyśmy umieścić "() =>" przed wszystkim, co musiało być leniwym. To powiedziawszy, chociaż jest to tylko funkcja0 w czasie wykonywania, byłoby problematyczne na poziomie systemu pisania, gdybyś mógł zdefiniować coś innego niż parametr o typie by-name. Pamiętaj również, że cechy FunctionN są przeznaczone głównie do implementacji i interoperacyjności Java, ponieważ nie ma czegoś takiego jak typ funkcji w Javie i JVM.

Będąc wyraźny

Jeśli nie muszą być jednoznaczne w swojej wpisując następujące pozwoli Ci być restrykcyjna

def g(x: => Int) = x * x 
val ggg: (=> Int) => Int = g _ 

bardziej złożonych typowania

The według nazwy pisanie może być używane tylko w części parametrów deklaracji typu funkcji. Same typy funkcji mogą być następnie używane w innych parametryzowanych typach.

var funks: List[(=> Int) => Int] = Nil 
funks ::= ggg 
funks foreach { _ { println("hi"); 5 } } 
1

Odpowiedź Rexa Kerr na this question daje wskazówkę: Argument nazwy zostanie ostatecznie przekonwertowany na Function0, ale prawdopodobnie potraktowany specjalnie podczas kompilacji.

Można to sprawdzić:

scala> gg(sys.error("me")) 
java.lang.RuntimeException: me 
    at scala.sys.package$.error(package.scala:27) 
    at $anonfun$1.apply(<console>:10) 
    at $anonfun$1.apply(<console>:10) 
    at scala.Function0$class.apply$mcI$sp(Function0.scala:34) 
    at scala.runtime.AbstractFunction0.apply$mcI$sp 
    ... 

EDIT

Aby przedłużyć na moim pierwszym komentarzu, oznacza to również, że nie można dać typ dla parametru ubocznych imię:

def test[A: Manifest](fun: Function1[A, Int]): Unit = 
    println("Found " + implicitly[Manifest[A]]) 

scala> test(gg) 
<console>:11: error: No Manifest available for => Int. 
       test(gg) 
       ^
+0

Oczywiście - nie mam wątpliwości, że to właśnie dzieje się pod maską.Ale jeśli chcę określić typ funkcji, zamiast pozwalać jej wywnioskować, co to dokładnie jest? Na pewno nie może być tak, że mogę stworzyć coś, w czym nie mogę napisać jego typu? Czy to możliwe ?! –

+0

Nie jestem pewien, czy możesz to zrobić. To znaczy, że faktycznie istnieje "typ" odpowiadający argumentowi o nazwie. Na przykład. 'gg.getClass.getMethods.find (_. getName ==" apply "). get uzyskuje również typ argumentu' Function0'. Ale w przypadku * kompilatora * ta konwersja jeszcze nie nastąpiła, więc mówi ci, że 'Function0' nie jest typem wymaganym (twoja próba przedostatnia). –

Powiązane problemy