Skip to content

Conversation

@odersky
Copy link
Contributor

@odersky odersky commented Nov 13, 2025

Based on #24352

* result of pure operation `op`, turning them into immutable types.
*/
@experimental
def immutable[T](op: -> T): T = op
Copy link
Member

@bishabosha bishabosha Nov 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to me this sounds like a block where mutability should be prevented on the inside, (i.e. like its some pure scope). Alternatively this has an effect similar to "freeze" in other languages (i.e the result is now immutable).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was unsure as well. I also thought of freeze before. Do you know which other languages have a construct like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I renamed to freeze. A search showed that it is used like this in Javascript and Haskell and it was proposed for beta.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the mechanism in most cases is assignment to an immutable variable (const in C++, let in Swift, Rust), so not a property of the type. Javascript has Objects.freeze. .NET has a Freezable abstract class

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ruby's root Object class also has a universal freeze method

@odersky odersky force-pushed the add-immutable branch 2 times, most recently from 23f66a6 to 4a17d29 Compare November 14, 2025 11:24
@odersky odersky changed the title Add immutable wrapper Add freeze wrapper Nov 14, 2025
* result of pure operation `op`, turning them into immutable types.
*/
@experimental
def immutable[T](op: -> T): T = op
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not yet renamed to freeze.

@Linyxus
Copy link
Contributor

Linyxus commented Nov 14, 2025

A soundness issue related to boxes:

import language.experimental.captureChecking
import language.experimental.separationChecking
import caps.{Mutable, immutable}

// A mutable ref and its immutable version
class Ref extends Mutable:
  private var data: Int = 0
  def get: Int = data
  update def set(x: Int): Unit = data = x
def allocRef(): Ref^ = Ref()
type IRef = Ref^{}

// Boxes
case class Box[+T](unbox: T)

// Parallelism
def par(op1: () => Unit, op2: () => Unit): Unit = ()

def test1(): Unit =
  val a = allocRef()
  val xs = immutable:
    Box(a)
  val b = xs.unbox
  par(() => a.set(42), () => println(b.get))  // data race! boom

@Linyxus
Copy link
Contributor

Linyxus commented Nov 14, 2025

Another counter-example related to double-flips:

import language.experimental.captureChecking
import language.experimental.separationChecking
import caps.{Mutable, immutable}

// A mutable ref and its immutable version
class Ref extends Mutable:
  private var data: Int = 0
  def get: Int = data
  update def set(x: Int): Unit = data = x
def allocRef(): Ref^ = Ref()
type IRef = Ref^{}

def test1(): Unit =
  val magic = immutable: // should be error, but ok
    (x: Ref^) => (op: Ref^ => IRef) => op(x)
  val reallybad: Ref^ -> Ref^{} = x => magic(x)(x => x)

reallybad says that: give me any tracked Refs, I will erase the capture for you!

@odersky
Copy link
Contributor Author

odersky commented Nov 14, 2025

@Linyxus Can you try to make this work to fix the unsoundness?

@natsukagami
Copy link
Contributor

Is the first example compiling because putting a into a Box is a pure operation (doesn't charge a)?

@Linyxus
Copy link
Contributor

Linyxus commented Nov 17, 2025

I get a MiMa error for the new freeze definition. What is the proper way to fix it?

@odersky
Copy link
Contributor Author

odersky commented Nov 17, 2025

MiMa should have suggested a filter to add to MiMaFilters.scala

@Linyxus Linyxus assigned odersky and unassigned Linyxus Nov 17, 2025
@Linyxus
Copy link
Contributor

Linyxus commented Nov 17, 2025

@odersky Ready for review!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants