FWIW, Here is an image showing the full stack up to Raven, though the BufferListLayer (stack item 2) is no longer present.
Thank you for your observations, Andrew. Allow me to take a moment to describe how differences appear in Smalltalk and Java, you have hit the nail squarely. I wish to mention two differences, one on the send side and the other on the resolve side. I also need to mention that it has been a number of years since I have had the Java version functioning, as I delved into making ParrotTalk work binary compatible between Smalltalk and Java. So I am rusty on how the Java side actually works. I just tested and the Reactor interface is not yet functional, so more work needed there.
On the send side, Java does not have a doesNotUnderstand: mechanism, while Smalltalk does. I do not use special syntax for local async sends, like NewSpeak does, I just have a doesNotUnderstand: implementation in the ERef hierarchy, so when you write: 42 eventual * 10, it sends the * message as an eventual send rather than an immediate call. In Java you have to explicitly call redirectMessage... Here is code (not yet functional in Raven for Java) that demonstrates both an explicit redirectMessage, as well as whenResolved with a ReactorInterface anonymous class implementation.
Ref ref = RefUtil.wrap("a pauwau is the best", Vat.getLocalVat());
Ref newRef = ref.redirectMessage("indexOf", "best");
newRef.whenResolved(new ReactorInterface<Integer>() {
@Override
public void value(Integer obj) throws NotResolvedException {
isResolved = true;
assertEquals(16, obj.intValue());
}
@Override
public void reactToLostClient(Exception e) throws NotResolvedException {
assertTrue(false);
}
});
To your point on whenResolved differences between Smalltalk and Java, exactly. In Smalltalk all you need to do is pass a one-arg block closure with #whenResolved: to an eventual ERef: promise, remotePromise, nearRef or farRef. FarRef and RemotePromise will forward this to the remote vat.
In Java, I am trying to use anonymous subclasses with a Templated Class for the expected return.
More difficulty arises if there is more that one expected return type. In Smalltalk just query the class of the returned type within the block closure. It is also possible to have two or more whenResolved: reactor blocks, each with a test for a specific type or failure, and the failure of the resolved reactor should not break the eventual reference, just fail quietly until a successful reactor is encountered.
In Java, you can have more than one reactor, with two different templated classes. The wrong cast should fail until the correct cast is found. Another thought is how scala works. I do not recall the name of the pattern matching by type, but in scala one can add more tha n one type to the pattern matching (I think it is pattern matching) and the correct code will be called. Perhaps Java's implementation of Raven needs scala code.
I hope this helps understanding and answers your concerns regarding differences in return types from eventual sends, between a dynamic type system such as Smalltalk and static return types in Java. Please note there is also a Swift implementation of ParrotTalk.
- HH
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
One thing that occurred to me is that the whenResolved etc. handlers only need to be customized to the degree that you want to interop with Java types that are not self-describing, such as the faked primitives and interfaces. Smalltalk types are uniformly self describing, allowing you to implement a single handler for all types, whereas only certain kinds of Java types are self-describing.
If you think about it in terms of RMI, which is not really much different other than the metaphor not taking time and frequency of non-response into account, the promises and futures are much like stubs and skeletons, proxies for the remote message send and resulting method invocations/responses. A Smalltalk to Smalltalk interop such as SST, which is very similar to RMI, only needs one proxy for all objects, which makes abstracting it from the transport (and whether the transport is continuous etc.) much easier. i.e. in RMI you need pools of constant TCP connections, while in SST the transport needen’t be a continuous connection, it could be entirely discrete such as http, and only one abstract connection between “spaces” or “nodes” is required.
If you avoid the nasties in Java and only support interop for self-describing types, you shouldn’t need to write so much custom but rote code. If you do need to support non self-describing types, only on those do you need the custom rote code in the handlers. Further if you use only one set of proxies, i.e. one future/promise/handler set, it will be much easier to keep Raven clean and separated from the transport implementation.
If you keep in mind that although it’s a useful and powerful metaphor, the notion of futures, promises and the like is only a metaphor for invoking remote methods, which in turn is only a more mentally adhesive manner of describing remote procedure calls, with self-describing objects one handler can invoke the appropriate methods on the object by delegating the responsibility to the object itself.
Andrew
Sent: Saturday, March 31, 2018 3:46 PM
Subject: Re: [Pharo-users] Regarding Raven: DOCap System
Raven is a remote, distributed object-capability system. I wanted to stress it runs distributedly over 2048-bit encrypted ParrotTalk 3.6
Sent from ProtonMail Mobile
Sent from ProtonMail Mobile