Scala: Function0 vs by-name parameters

Can someone give a definitive answer on how the parameters <username>>> => T and Function0 () => T converted into each other by the Scala compiler? I know that they are not the same thing, but the difference is very subtle, as they can be used interchangeably in many scenarios.

Example: if I define

 def someFunction: Int = 2 def f(x: => Int): Unit = println(x) 

then i can call successfully

 f(2) f(someFunction) 

How () => Int an acceptable replacement for => Int ?

In the general case, () => T universally acceptable replacement for the parameter => T named <

Also, please correct me if I am mistaken in the following reasoning: => T never an acceptable substitute for () => T , because the first type of value is ( T ), the other is the type of function. That is, if I have def f(x: () => Int) , I can never pass Int or lazy Int (it doesn't even make sense, since there are no lazy types).

+5
source share
1 answer

Well, here's the full breakdown.

 def value: Int = ??? def method(): Int = ??? def f1(f: () => Int) = ??? def f2(f: => Int) = ??? f1(value) // fails f1(method) // works f2(value) // works f2(method) // works with a warning "empty-paren method accessed as parameterless" 
  • f1 (value)

This error fails because f1 expects the function Unit => Int, but it is assigned the value Int.

  1. f1 (method)

This works because f1 expects a function and a method is given. Here's the difference: the method is not a value in Scala; it cannot exist by itself and is an attribute of the context that it defines (class, trait, object, etc.). Function is the value; it can be stored in a collection, taken as an argument in another function, returned from a function, etc. When the compiler expects a function and receives a method, it executes the eta extension. Given the function, for example. f: (a: Int, b: Int) => Int , this extension is the process of creating another layer around it, keeping the signature, so it becomes (a: Int, b: Int) => f(a, b) . This method is useful because we can turn methods into a function. For some method, def f(a: Int): Int = ??? we can execute an eta extension to create a function of the type Int => Int: (a: Int) => f(a) . I wrote a blog post about this a while ago, if you're interested.

  1. 2 (value)

It works without surprises, but note that the passed value is accessed every time it is used in the function body.

  1. 2 (method)

It works, but with a warning that we are calling a method that is given with an empty bracket without using parentheses. It is good practice to use methods without parentheses (e.g. f ), when they simply represent a value, but one that is recounted every time it is accessed, for example. numberOfUpvotes and use methods with an empty bracket (for example, f() ) when some side effect is performed, and therefore the method is not idempotent, for example. createSnapshot() (again, this should not be present in purely functional code).

Tip: do not burden your mind with what to replace for. Do not use replacements. If something needs a function, provide it with a function. If a value is required for this, specify a value. If the method is defined without parsers, call it without parsers. If it has parens, call it with parens.

If you need to switch from a method to a function, and the compiler expects a function, this extension will happen automatically. If it does not expect a function, you need to do it manually.

 def f(): Int = ??? val a = f // no function context; a is a string val b: () => Int = f // b is a function Unit => Int val c = f2 _ // c is a function Unit => Int 

The latter case is a partially applied function. I feel like I'm too wide right now, so I will stay here. Hope this helps.

+3
source

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


All Articles