Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
505 views
in Technique[技术] by (71.8m points)

functional programming - Scala: Function0 vs by-name parameters

Can anyone give a definitive answer on how by-name parameters => T and Function0 parameters () => T are transformed into one another by the Scala compiler? I know they are not the same, but the difference is very subtle as they can be interchangeably used in many scenarios.

Example: if I define

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

then I can successfully call

f(2)
f(someFunction)

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

More generally, is () => T a universally acceptable replacement for a by-name => T parameter?

Also, please correct me if I'm wrong on the following reasoning: => T is never an acceptable replacement for () => T because the first is a value type (T), the other is a function type. That is, if I have def f(x: () => Int), I'll never be able to pass an Int, or a lazy Int (doesn't even make sense anyway as there are no lazy types).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Alright, 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"
  1. f1(value)

This one fails because f1 is expecting a Unit => Int function, but is given an Int value.

  1. f1(method)

This one works because f1 is expecting a function, and is given a method. Here's the difference: method is not a value in Scala; it cannot exist on its own and is an attribute of the context it's defined in (class, trait, object etc.). Function is a value; it can be kept in a collection, taken as argument in another function, returned from a function etc. When the compiler is expecting a function and is given a method, it performs eta expansion. Given a function e.g. f: (a: Int, b: Int) => Int, eta expansion is a process of creation of another layer around that while preserving the signature, so it becomes (a: Int, b: Int) => f(a, b). This technique is useful because we can turn methods into function. Given some method def f(a: Int): Int = ???, we can perform eta-expansion to create a function of type Int => Int out of it: (a: Int) => f(a). I wrote a blog post about this some time ago if you're interested.

  1. f2(value)

Works without surprises, but pay attention to the fact that the passed value is accessed every time it's used in the function body.

  1. f2(method)

Works, but with a warning that we are invoking a method that is defined with empty parenthesis by using no parenthesis. Good practice is to use methods without parenthesis (e.g. f) when they simply represent a value, but one that is recalculated every time it's accessed, e.g. numberOfUpvotes, and to use methods with empty parenthesis (e.g. f()) when some kind of side-effect is performed and hence the method isn't idempotent, e.g. createSnapshot() (again, this should not be present in purely functional code).

Word of advice: don't encumber your mind with what's a replacement for what. Don't use replacements. If something needs a function, provide it a function. If it needs a value, provide a value. If a method is defined without parens, invoke it without parens. If it has parens, invoke it with parens.

If you need to go from method to function and compiler is expecting a function, eta-expansion will happen automatically. If it's not expecting 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

Last case is a partially applied function. I feel like I'm going too broad now so I will stop here. I hope this helped.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...