Scala types and methods of methods as parameters

In the following code example, I don’t understand why the fun function can be passed as an argument to the addAction method. The fun method is of type Unit , while the addAction method expects a function of type () => Unit .

If fun is of type () => Unit , then why does the compiler complain that fun is of type Unit when I try to add fun to the list of actions: 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 OK // actions = fun :: actions actions = (() => fun) :: actions // OK // I would expect the same compiler error here (found: Unit, required: () => Unit), but it OK why? addAction(fun) actions.foreach(_()) // prints twice "fun1 executed" } } 
+6
source share
3 answers

Take this as an introductory example:

 def fun() { println("fun1 executed.") } val a1 = fun val a2: () => Unit = fun 

Both lines are compiled and (thanks to type inference) look equivalent. However, a1 is of type Unit , and a2 is of type () => Unit ... How is this possible?

Since you do not explicitly specify type a1 , compilers interpret fun as calling a fun method of type Unit , so type a1 same as type fun . It also means that this line will print fun1.

However, a2 has an explicitly declared type () => Unit . The compiler helps you here, and he understands that since the context requires a function of the type () => Unit , and you have provided a method corresponding to this type, it should not call this method, but consider it as a function of the first class!

You are not doomed to explicitly specify type a1 . Saying:

 val a1 = fun _ 

Now do you understand where your problem is?

+8
source

You need to write fun _ in the first case to avoid calling the method and executing the eta extension.

This will work:

 actions = (fun _) :: actions 

If you do not, fun is evaluated.

See Section 6.7 (Method Values) for more information. Scala Language Reference .

As for why fun not evaluated in the second case, this is because type inference can clearly conclude that addAction expects a function. By the way, the type fun technically ()Unit , not Unit , that is, the type of method, not the type of value. See Section 3.3.1 in the reference for more details.

+5
source

There is a difference between methods and functions. In your case actions is a list of functions. When the compiler knows that a function is required (for example, in the case of addAction ), it can automatically convert the fun method to a function. Now :: also a method, so the compiler also knows that it performs functions as parameters. But the problem is the syntactic sugar of the right-associative operator :: . If you name it as a method: actions.::(fun) it will compile (although I cannot check it at the moment). When writing fun :: actions compiler considers fun be an expression and therefore evaluates it, and since it "returns" a Unit , you get your compiler error.

EDIT

Since now I have the opportunity to test my hypothesis (which was wrong), here are your options:

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

Source: https://habr.com/ru/post/898852/


All Articles