0
def f(x: Int): Boolean = (x >= 0 && x < 4)

List(1, 3, 5).map(f)  // List(true, true, false)
f                     // does not compile

Why can f be used where a function value is expected, even if it is not a function value itself?

colorblind
  • 158
  • 2
  • 7

1 Answers1

1

What is happening?

In places where a function type is expected, f is converted to an anonymous function (x: Int) => f(x).

def f(x: Int): Boolean = (x >= 0 && x < 4)
// f                    // f itself is not a function value
f(_)                    // f(_) is an anonymous function
List(1, 3, 5).map(f)    // f is converted to f(_) in places where a function type is expected
List(1, 3, 5).map(f(_)) // equivalent to last line

Why is f not a function value in the first place?

  • Because the parameterless function f was not defined. A curried (parameterless) function value would work:
val g = (x: Int) => (x >= 0 && x < 4)
g

Why is f accepted as a function value?

  • map expects a function type and because curried and uncurried versions of f f and g both do the same, the automatic conversion makes sense.
  • another advantage is that map(f) has a cleaner look than map(f(_)).
  • a disadvantage of all the automatic and syntactic sugar stuff that is done for you, is that it can be confusing
colorblind
  • 158
  • 2
  • 7
  • 2
    Note that the process of turning a **method** into a **function** is called `eta-expansion`. – Luis Miguel Mejía Suárez May 05 '20 at 16:04
  • Thank you, this is the core of the question. Often, in my thinking I'm stuck for the same reasons: I don't know what the compiler does. I think it's time to look more closely into the topic of compilers if I want to get on the next level. – colorblind May 05 '20 at 16:46
  • 2
    Being pedantic, that's not a *curried* function, it's just a function value rather than a method. A curried function is one that is the result of taking a function with multiple arguments and turning it into a set of nested HOF. – Tim May 05 '20 at 16:47
  • 2
    @colorblind _" I think it's time to look more closely into the topic of compilers if I want to get on the next level"_ I do not think so. You really do not need to known what the compiler does and how. You usually just need to know a few tricks / syntactic sugar / rules it has. For example in this case, just knowing the compiler will convert a method to a function it one is expected should be enough to continue with your learning. – Luis Miguel Mejía Suárez May 05 '20 at 16:49
  • @LuisMiguelMejíaSuárez I appreciate the opinion. I thought about it and saw that in IntelliJ I can desugarize scala code, which may help me in the situations when I don't see that there is some "trick" involved. – colorblind May 05 '20 at 17:15
  • 2
    @colorblind You can also printout different compiler [phases](https://typelevel.org/scala/docs/phases.html), for example, `scala -Xprint:typer foo.scala`. You can do this even in real-time inside REPL `:settings -Xprint:typer`. Now right after entering some Scala source, it will printout the phase. You can even see the bytecode with `:javap -`. You can also inline it with `scala -Xprint:typer -e "for { i <- List(1,2,3) if i > 2 } yield { i.toString }"` – Mario Galic May 05 '20 at 18:16
  • Thanks so much, I'm very interested in this! – colorblind May 05 '20 at 18:21