Hi Andreas,
This is great! I think this is actually what DCI needs most of
all—more people talking about it and explaining it in the wider
community.
As for TC39, I agree that that would be best, but do think it will be a huge challenge. I followed the development of one of the more recent additions to JS—class fields—pretty closely, and the process of incorporating even small fixes into the spec based on community feedback was quite difficult. (Of course that's better than the other extreme, where changes are made without sufficient vetting.) Anyway, I think the best first step would be to create a real implementation that works in JS (and TypeScript too if possible), and start a community behind it so people can see and experience the value of it. Even if it's still a small number of people initially, I think that would help when proposing it to TC39, and of course having a concrete implementation to share with the TC39 delegates would help.
My work on implementing true DCI in JS and TS is quite old at this point, but it could still serve as a useful starting point:
https://github.com/mbrowne/babel-dci
https://github.com/mbrowne/typescript-dci
The typescript-dci one was forked from the official TypeScript
compiler at the time.
One challenge with TypeScript—as you might already be aware—is
that it's now common to compile your code with Babel or one of the
newer, faster JS compilers (e.g. SWC and esbuild), but still use
the official TypeScript compiler for type-checking. So I'm not
sure which would be better to tackle first—the TS compiler so the
types work, or the JS compiler so that it also works for those not
using TS. In terms of the codebase, Babel would probably be the
easiest to work with. For a while they talked about adding
official support for parsing plugins to add new syntax, but the
last I saw they had decided against that, and they recommend just
forking the parser for this purpose (for my initial
implementation, I just imported some of their internals directly,
but that is no longer possible, and isn't an acceptable solution
for production anyway of course). But the good news is that you
wouldn't need to fork anything else in Babel—the actual code
transformation can be done in a Babel transform plugin. And BTW,
Babel is what the TC39 committee generally uses for
proof-of-concept implementations of new proposals.
I don't have much time these days to put more work into these projects, but I'd be happy to be involved where I can, offer feedback and tips, etc.
Cheers,
Matt
P.S. I'll take a more detailed look at your blog post later. The
coding approach just using functions and prefix naming looks good.
--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/object-composition/4e4b70d8-4d64-40a0-a0ad-6296153cf695n%40googlegroups.com.
Did you also consider and explore a mix-ins approach as an alternative to the more function-based approach that you took?
Did you also consider and explore a mix-ins approach as an alternative to the more function-based approach that you took?I did, but mixins in typescript/javascript are either class-based, which means that objects cannot be instantiated earlier, or they are added at runtime, which makes unbinding difficult, and because of that it creates a growing list of methods on the object for every Context it participates in, and you also have to solve the issue of name collisions. I wanted to get close to the transient nature of Roles; that they only exist when the Context is executing.
With my approach the objects won't have their RoleMethods attached even when they're inside the Context, so it's not perfect, but I think this is the best compromise. How does it work in trygve under the hood - do the RoleMethods become a part of the Role-player while inside the Context, and in that case, how do you handle name collisions?
--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/object-composition/F02AE81B-4963-4579-9E97-BC1F9C4CA5DD%40gmail.com.
Another thing to keep in mind with JS/TS is that asynchronous code is very common - more common than it is in many other languages - so efforts to automatically unbind role methods somehow after context execution completes are of limited usefulness. (When I say "automatically" I'm still referring to using the language itself without source transformation - with source transformation this issue can be solved. See this for the manual technique that could be done under the hood in a simpler way by a transpiler.)
On another note, it occurred to me that if one were willing to accept the possibility of name collisions as a drawback, one could alternatively just forget about unbinding role methods and rely on the type system to prevent role methods from being called where they shouldn't be—like Cope's C++ implementation. But unlike the C++ implementation, you could theoretically at least wait to add the role methods in the first place until they're needed. In other words, method injection without method un-injection. So TypeScript potentially opens up a new implementation option that you wouldn't have in plain JS. I wonder if the name collision issue would become any more problematic in idiomatic JS code than it is with the C++ implementation. (I should add that I haven't personally tried this approach and I'm not sure how difficult it would be to implement in TypeScript.)
Personally I would rather avoid the name collision issue entirely
and focus on a source transformation solution, but it's
interesting to think about alternatives.
To view this discussion on the web visit https://groups.google.com/d/msgid/object-composition/A74CBEF4-8D93-4206-91E8-9203CB120FFC%40gmail.com.
On 8 Nov 2022, at 11.44, Matthew Browne <mbro...@gmail.com> wrote:Personally I would rather avoid the name collision issue entirely and focus on a source transformation solution, but it's interesting to think about alternatives.
I meant as the DCI programmer (user), not the author of the DCI
implementation. Like how if I'm programming in trgve, I don't have
to worry about choosing a role method name that won't conflict
with something else.
Maybe a bit more on name collisions:I can detect almost all name collisions at compile time. The one exception is when an object plays multiple roles. I can catch that, too, but it is a run-time warning. Name collisions are never fatal (I think).
For those I can catch at compile time, I choose “the most obvious choice” and issue a warning.
On 9 Nov 2022, at 10.15, Andreas Söderlund <gaz...@gmail.com> wrote:tisdag 8 november 2022 kl. 11:30:03 UTC+1 skrev Cope:Maybe a bit more on name collisions:I can detect almost all name collisions at compile time. The one exception is when an object plays multiple roles. I can catch that, too, but it is a run-time warning. Name collisions are never fatal (I think).Why isn't it possible to get the object information at compile-time in that case?
I see that in tests/roletest3.k it's a compile error to have the same RoleMethod name as in the object. Signature mismatches seems to be checked only afterwards, as a warning.
I cannot say I've found a decent solution to this. With a private var instead of a Role you'll get convenient access, so maybe an interface is better to access data. Any thoughts?
I’m beginning to dislike context member data and am moving more to using role bindings. Accessing naked member data of a context opens the door to all the polymorphism probllems DCI was designed to solve.
As for MVC, using the trygve construct to pull data initerface signatures from the requires section into the public role interface perfectly communicates what you want to communicate about the data’s part (role) in the view / model contract.
On 9 Nov 2022, at 19.36, Andreas Söderlund <gaz...@gmail.com> wrote:But how to do that without name collisions? If the contract defines String email(), I cannot use email as the name of a RoleMethod.
Creating names may be the hardest part of programming, but we’re up to it. Both of these APIs do different things; let the names reflect that. I think “email” is a terrible method name, created by someone who hates typing. What does it do? Read a message and give me its content? Give me someone’s email address? We can do better than this.
On 9 Nov 2022, at 21.38, Andreas Söderlund <gaz...@gmail.com> wrote:Sure, but this may relate to the Data vs. Context distinction that Rune talked about. If I want to simply display data in a MVC context, and a Person data structure has an email, at least it should have a getter-like property for that, right? What to call it, and how to expose that data to Contexts? Is that purely a naming issue in a specific Context? I thought MVC had this sorted out already.An alternative is maybe to avoid it being exposed at all, instead doing as in the UserList example I linked to: https://pastecode.io/s/qfp84uw4
FWIW, I've done a few experiments using contexts and roles to
implement MVC, and I never came up with anything that I felt was
much better than using standard classes for that. I noticed that
Trygve's Prokon example also mainly sticks to regular classes for
the UI part of the app. I wonder if this is because MVC (or at
least the V and the C) is more of an "atomic event architecture"
to use a term from Cope's Lean Architecture book, rather than the
type of system (or part of a system) with more complex data/role
mapping. I realize that you certainly can have a data
object play the role of a View, I just haven't found it to be very
useful in practice, nor have I seen any sizeable/realistic
examples of this. Side-note: the context itself would sort of
become the controller.
(I don't think this goes against what I was saying earlier about having only contexts and no classes; you could substitute "classes" with "context declarations with no roles" in the above paragraph for the same essential concept.)
But perhaps I'm missing something?
--
You received this message because you are subscribed to the Google Groups "object-composition" group.
To unsubscribe from this group and stop receiving emails from it, send an email to object-composit...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/object-composition/B632F7B1-ACE8-4E58-8C19-37D2AA97E872%40gmail.com.
Hi Andreas,
I was thinking about the code examples in the tutorial, and some
alternative syntax options that might be worth considering. I'm
not sure if these are an improvement or not, but they feel more
role-like to me even though of course they don't get any closer to
a unified data/role object.
Option 1:
Option 2:
I'm not sure if making the roles into objects would make it more
likely for the programmer to try to use this and become confused by it, but
we should keep in mind that this doesn't work with the current examples in
the tutorial anyway. It could be made to work by using .call() or
.apply() when calling role methods, but that would best be done by
a real transpiler, so I totally understand and agree with the
decision not to do that to keep things simpler for the programmer.
One thing I do prefer about the current technique in the
tutorial—and maybe the tutorial could be updated to take advantage
of this—is that you could use function hoisting so that you don't
have to scroll past all the roles in order to see the method call
that kicks off the Context, like this:
Just some food for thought.
Congrats on writing such a thorough tutorial, with four parts so
far!
-Matt