Whenever Actor calls uncontrolled code (through overridden method or closure) a callee has access to Actor's locals. We can encapsulate this by never calling uncontrolled code and accessing locals from the same Actor. For example, we can create storage actor and send him get and set messages, and otherwise leave the code as it is. On minus-side here is performance and possible need for sys::Unsafe to store mutables.
What I want is enforceable encapsulation.
For now the only solution I have is to pass locals as additional argument to concurrent::Actor.receive.
brianTue 31 Aug 2010
I am not sure I fully follow this discussion.
The language will guarantee encapsulation of mutable state between threads (unless of course you use Unsafe).
If you want to create/manage immutable state on an actor you can use const fields.
Then for mutable state, you have to manage that within the actor. A common technique I use is something like this:
const class PublicService
{
new make(ActorPool pool, ImmutableState state)
{
this.state = state
this.actor = Actor(pool) |msg| { receive(msg) }
}
Void msg1() { actor.send(...) }
Void msg2() { actor.send(...) }
private Obj? receive(Obj? msg)
{
MutableHandler h := Actor.locals.getOrAdd("state", MutableHandler(this))
return h.receive(msg)
}
internal const ImmutableState state
internal const Actor actor
}
internal class MutableHandler
{
new make(PublicService) { ... }
}
vkuzkokovTue 31 Aug 2010
if PublicService has something like
Void msg1(|->| func) { actor.send(func) }
MutableHandler will expose Actor.locals["state"] to func if func is called in MutableHandler without wrapping to another actor.
brianTue 31 Aug 2010
MutableHandler will expose Actor.locals["state"] to func if func is called in MutableHandler without wrapping to another actor.
What happens with functions is similar to collections. Functions which access (close over) mutable state can't actually be passed b/w actors. If the function itself is immutable then it can be passed between actors. See Functions chapter.
Now the second issue (which is what you are probably getting at), is that once the function is called in the actor's context, that function could potentially access Actor.locals which would be considered private state. But this is only a problem assuming:
you know the private key used in the locals map
you know the data structure used in the locals map
the data structure used in the locals map is actually public
So I would consider Actor.locals to be encapsulated from a pragmatic perspective. But Fantom doesn't absolutely enforce encapsulation if you really want to break the documented rules. For example you can use the "->" operator to access internal/private slots.
So in some sense Fantom relies on conventions. You can access Actor.local state or private slots, but you do so knowingly that you are breaking the encapsulation of the public API and there is no guarantee that you are using a published, stable interface.
vkuzkokovTue 31 Aug 2010
For example you can use the "->" operator to access internal/private slots.
Bad thing is that unlike reflection it doesn't even look like dirty hack. I guess that's price we pay for dynamicity.
As far as I remember Java checks access mode even while in reflection. To overcome it you should use AccessController.doPrivileged, which is already double dirty hack and makes you think at least twice before accessing others' privates.
I was going to plunge into this feature tonight, but I still have an uneasy feeling about adding this complexity to the language. I kind of feel like maybe we ought to wait and get some more experience under our belt, so I'll probably chew off something else next.
@brian "Tonight" was 4/24/2008. How do you feel about this feature now?
brianSun 12 Sep 2010
I still think something like it would be very useful. On the flip side, I also feel it is something easily and safely added in a future release, so I think the prudent path is to avoid adding it right now.
MoOmSun 12 Sep 2010
I couldn't agree more, it would make code dealing with Actors a lot more readable.
vkuzkokov Tue 31 Aug 2010
Whenever Actor calls uncontrolled code (through overridden method or closure) a callee has access to Actor's locals. We can encapsulate this by never calling uncontrolled code and accessing locals from the same Actor. For example, we can create storage actor and send him
get
andset
messages, and otherwise leave the code as it is. On minus-side here is performance and possible need forsys::Unsafe
to store mutables.What I want is enforceable encapsulation.
For now the only solution I have is to pass locals as additional argument to
concurrent::Actor.receive
.brian Tue 31 Aug 2010
I am not sure I fully follow this discussion.
The language will guarantee encapsulation of mutable state between threads (unless of course you use Unsafe).
If you want to create/manage immutable state on an actor you can use const fields.
Then for mutable state, you have to manage that within the actor. A common technique I use is something like this:
vkuzkokov Tue 31 Aug 2010
if
PublicService
has something likeMutableHandler
will exposeActor.locals["state"]
tofunc
iffunc
is called inMutableHandler
without wrapping to another actor.brian Tue 31 Aug 2010
What happens with functions is similar to collections. Functions which access (close over) mutable state can't actually be passed b/w actors. If the function itself is immutable then it can be passed between actors. See Functions chapter.
Now the second issue (which is what you are probably getting at), is that once the function is called in the actor's context, that function could potentially access
Actor.locals
which would be considered private state. But this is only a problem assuming:So I would consider Actor.locals to be encapsulated from a pragmatic perspective. But Fantom doesn't absolutely enforce encapsulation if you really want to break the documented rules. For example you can use the "->" operator to access internal/private slots.
So in some sense Fantom relies on conventions. You can access Actor.local state or private slots, but you do so knowingly that you are breaking the encapsulation of the public API and there is no guarantee that you are using a published, stable interface.
vkuzkokov Tue 31 Aug 2010
Bad thing is that unlike reflection it doesn't even look like dirty hack. I guess that's price we pay for dynamicity.
As far as I remember Java checks access mode even while in reflection. To overcome it you should use AccessController.doPrivileged, which is already double dirty hack and makes you think at least twice before accessing others' privates.
tcolar Tue 31 Aug 2010
Yeah, never mess with other's privates !
vkuzkokov Sun 12 Sep 2010
I started this topic before I read thread locals proposal.
@brian "Tonight" was 4/24/2008. How do you feel about this feature now?
brian Sun 12 Sep 2010
I still think something like it would be very useful. On the flip side, I also feel it is something easily and safely added in a future release, so I think the prudent path is to avoid adding it right now.
MoOm Sun 12 Sep 2010
I couldn't agree more, it would make code dealing with Actors a lot more readable.