scalaammonite

How to interpret function value wrapped in curly braces (ammonite issue)


After reading What is the formal difference in Scala between braces and parentheses, and when should they be used?, I still don't know how to understand function value wrapped in {}.

Consider the following two REPL sessions:

@ val f = { (x: Int) =>
    x
    val y = x
    y
  }
f: Int => Int = ammonite.$sess.cmd30$$$Lambda$1765/0x0000000801346840@24c7b944
@ { (x: Int) =>
    x
    val y = x
    y
  }
cmd31.sc:3: not found: value x
  val y = x
          ^
Compilation Failed

I have several questions.

  1. Why the first snippet compiles while the second doesn't? In the first snippet, compiler knows the {...} as a whole is a function value. In the second snippet, only the (x: Int) => \n x part is function value (Sorry about the \n to indicate line break). Why?
  2. Regarding the { (x: Int) => \n ... }, when is it interpreted as function value and when is it not?
  3. Is the curly brace ({}) part of the function value, or only (...) => ... inside is function value? If it is a part it, does the form have a name? For example, I think (_ + _) could be called function value's placeholder syntax.

Update: This is purely an Ammonite issue. See answer for detail.


Solution

  • The problem here is ammonite.

    Scala REPL has a paste mode allowing you to paste multiple lines before evaluating:

    :paste
    sealed trait X
    class Implementation extends X // impossible without doing it at once
    // Ctrl+D
    

    Ammonite doesn't have such paste mode, but it allows you to perform multi-line copy paste... but wrapping them all in {} which ammonite would unwrap. So your code:

    { (x: Int) =>
      x
      val y = x
      y
    }
    

    is seen by compiler as

    (x: Int) =>
      x // end of function definition
    val y = x // new variable calling x, which isn't defined anywhere
    y // call to undefined y, because previous line failed
    

    The first example works because you have val f = so ammonite's parser cannot assume that all of your code is within one block, so it doesn't strip it before passing it into compiler.

    As docs suggest if you don't want this behavior you should add another layer of curly brackets:

    {{ (x: Int) =>
      x
      val y = x
      y
    }}
    

    This isn't a compiler and language specification issue as much as issue with some particular REPL implementation.