The Syntax of Capture Checking?

Hi,

I tried the sample code from Capture Checking with the bightly build 3.4.0-RC1-bin-20230804-73967d5-NIGHTLY. I got the following error:

Compiling project (Scala 3.4.0-RC1-bin-20230804-73967d5-NIGHTLY, JVM)
[error] ./CC.scala:14:27
[error] Illegal start of declaration
[error] def usingLogFile[T](op: ({*} FileOutputStream) => T): T =
[error]                           ^
[error] ./CC.scala:14:46
[error] an identifier expected, but ')' found
[error] def usingLogFile[T](op: ({*} FileOutputStream) => T): T =
[error]                                              ^
Error compiling project (Scala 3.4.0-RC1-bin-20230804-73967d5-NIGHTLY, JVM)

For the experimental language feature, is the syntaxt different from the document?

Thanks for your help!

Guofeng

Did you enable that language.experimental.captureChecking flag?

The syntax have changed, but it was not reflected in the docs

It works following the changed cc sytax.

But the following code pass the compilation:

val later = usingLogFile { f => () => f.write(0) }

and got the following runtime error:

 ../scala-cli -S 3.4.0-RC1-bin-20230804-73967d5-NIGHTLY CC.scala

Compiling project (Scala 3.4.0-RC1-bin-20230804-73967d5-NIGHTLY, JVM)
Compiled project (Scala 3.4.0-RC1-bin-20230804-73967d5-NIGHTLY, JVM)
Exception in thread "main" java.io.IOException: Stream Closed
	at java.base/java.io.FileOutputStream.write(Native Method)
	at java.base/java.io.FileOutputStream.write(FileOutputStream.java:322)
	at CC$package$.$anonfun$1$$anonfun$1(CC.scala:11)
	at CC$package$.shouldGetAStaticErrorAsDocumented(CC.scala:12)
	at CC$package$.CC(CC.scala:19)
	at CC.main(CC.scala:14)

The following is the code I use:

import java.io.FileOutputStream
import language.experimental.captureChecking

def usingLogFile[T](op: FileOutputStream^ => T): T =
  val logFile = FileOutputStream("log")
  val result = op(logFile)
  logFile.close()
  result

def shouldGetAStaticErrorAsDocumented =
  val later = usingLogFile { file => (x:Int) => file.write(x) }
  later(0) // crash

@main def CC: Unit =
  val xs = usingLogFile { f =>
    List(1, 2, 3).map { x => f.write("x\n".getBytes()); x * x }
  }
  
  shouldGetAStaticErrorAsDocumented

It is ok to me. I just want to confirm here if it is just an issue of the NIGHTLY Scala 3 compiler.

Thanks for all your helps!

Guofeng

I don’t think this is definitive, but the current version of the capture checker requires the use of sealed on the type parameter to prevent impure functions from escaping:
def usingLogFile[sealed T](op: FileOutputStream^ => T): T =

11 |  val later = usingLogFile { file => (x:Int) => file.write(x) }
   |              ^^^^^^^^^^^^
   |Sealed type variable T cannot be instantiated to box (x$0: Int) => Unit since
   |that type captures the root capability `cap`.
   |This is often caused by a local capability in an argument of method usingLogFile
   |leaking as part of its result.