#340 Closures and immutability

cgrinds Thu 21 Aug 2008

I was somewhat surprised that the closure in the following code isn't immutable.

const class Several {
  const Thread server
  Void go() {
    clo := |,| {echo("$server")}
    echo("Clo is immutable? $clo.isImmutable")
  }
}

when run this prints:

Clo is immutable? false

From the docs

closures which don't capture any variables from their scope are automatically const

What wasn't clear was the exact definition of scope in this sentence. Scope in the context of closures typically means the scope that captures local variables.

brian Thu 21 Aug 2008

The compiler isn't quite that smart yet. What the compiler does in that situation is capture the implicit this (which in turn gives us access to this.server).

Right now if I capture any locals I mark it as mutable, but I think I need to revisit that - in this case the only locals I'm capturing are themselves immutable, so it should be safe to say the clo is also immutable. I will take a look into this.

There is also another problem that multiple closures in one method share the same cvars (local variables pulled out into heap). Because of that one non-immutable closure will cause the other potential immutable closure to be mutable. That problem is a little more complicated. It is probably ok for now, but something to keep in mind. If you run into the work around is to use the curry operator or separate your closures into two methods.

cgrinds Tue 26 Aug 2008

Thanks Brian - I assume this change didn't make it into 1.0.31 right? I also assume that's why this won't work? This is making using the concurrency stuff harder than it should be.

abstract class ActorA {
  const Thread thread
  new make(Str name){
    thread = Thread(name, &run).start
  }

  abstract Obj eachLoop(Obj msg)

  Void run(Thread t) {
    t.loop(&eachLoop)
  }

  Void sendAsync(Obj obj) {
    thread.sendAsync(obj)
  }
}
sys::NotImmutableErr: Run method not const: |sys::Thread->sys::Void|
  fan.sys.Thread.make$ (Thread.java:45)
  fan.sys.Thread.make (Thread.java:28)
  ActorExample_0::ActorA.make$ (/C:/stuff/fan/concurrency/fan/ActorExample.fan:30)
  ActorExample_0::Actor.make$ (/C:/stuff/fan/concurrency/fan/ActorExample.fan:19)
  ActorExample_0::Actor.make (/C:/stuff/fan/concurrency/fan/ActorExample.fan)
  ActorExample_0::ActorExample.go (/C:/stuff/fan/concurrency/fan/ActorExample.fan:10)
  ActorExample_0::ActorExample.main (/C:/stuff/fan/concurrency/fan/ActorExample.fan:6)

brian Tue 26 Aug 2008

No sorry - I haven't fixed the original problem you reported.

Although in this case that error is the correct behavior. Any instance method on a non-const class is mutable. If it is a static method, then the behavior should be correct today. And the change I need to make is that any instance method on a const class should be immutable.

cgrinds Tue 26 Aug 2008

Ah right - I knew that about instance methods. I'm finding it very interesting to try to progam with Fan's concurrency model. Of course making a class const makes it harder to manage it's state :-) Even state that's not shared.

So far I've been using namespaces and thread locals both of which make me feel a bit icky. I'll start new thread though to discuss those issues.

Login or Signup to reply.