On 13 May 2016, at 17:42, Matthew Browne <mbro...@gmail.com> wrote:class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}
class AnimalShelter {
public void putAnimal(Animal a) {
System.out.println("AnimalShelter.putAnimal");
}
}
class CatShelter extends AnimalShelter {
//does *not* override AnimalShelter.putAnimal()
public void putAnimal(Cat c) {
System.out.println("CatShelter.putAnimal");
}
}
class Test {
public Test(AnimalShelter shelter) {
shelter.putAnimal(new Dog());
}
}
{
//Outputs "AnimalShelter.putAnimal".
//Thanks to this behavior of the compiler, it's impossible to put a dog
//in a cat shelter.
new Test(new CatShelter());
}
--
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 post to this group, send email to object-co...@googlegroups.com.
Visit this group at https://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/d/optout.
I have a feeling that if we got rid of classes altogether we could achieve all of the design capabilities with contexts and dumb data and no inheritance. That's way over in a possible end game I know, so what would I want for trygve? I would still like to see how far we could take it without inheritance
On 13 May 2016, at 22:22, Raoul Duke <rao...@gmail.com> wrote:Hasn't there been discussion of how applicative / fp styles are actually better? :-)
On 13 May 2016, at 22:35, Matthew Browne <mbro...@gmail.com> wrote:I was just going to say something very similar...I still think that it would be better to get rid of classes completely, and provide some mechanism for Context specialization instead.
On 13 May 2016, at 21:40, Egon Elbre <egon...@gmail.com> wrote:Remove inheritance :)I suspect by not using inheritance there can be novel solutions: e.g.class AnimalShelter { ... }specialization CatShelter of AnimalShelter where Animal is Cat { ... }Where AnimalShelter and CatShelter are invariant and Animal is replaced in CatShelter recursively with Cat.
class Cat {}
class Dog {}class AnimalShelter <Animal> {
public void putAnimal(Animal a) {System.out.println("AnimalShelter.putAnimal")}}
class Test {public Test(AnimalShelter<Dog> shelter) {shelter.putAnimal(new Dog())}}new Test(new AnimalShelter<Cat>())
On 5/14/16 5:53 AM, James O Coplien wrote:
One option I've been thinking about is compiler-assisted forwarding, for example:
On 13 May 2016, at 22:35, Matthew Browne <mbro...@gmail.com> wrote:
I was just going to say something very similar...I still think that it would be better to get rid of classes completely, and provide some mechanism for Context specialization instead.
That just solves a different problem (and I like it) m´but it just moves this problem elsewhere. We’d still need to choose between one of the several flavours of Liskov.
The Go language was actually one of my inspirations for this. But I wanted to also have a way to call embedded methods directly, for convenience, hence the automatic forwarding.
On 14 May 2016, at 15:18, Egon Elbre <egon...@gmail.com> wrote:Yes and no. In there, Animal is a type parameter not an interface.
Cat/Dog have no relation between them.
And usually AnimalShelter<Dog> and AnimalShelter<Cat> are either covariant or contravariant.
On 14 May 2016, at 15:18, Egon Elbre <egon...@gmail.com> wrote:Yes and no. In there, Animal is a type parameter not an interface.So? There’s no semantic difference: only syntactic saccharin.
Cat/Dog have no relation between them.Nor do they in your example. Or do they? If they do, how do you express it, if not by inheritance?
And usually AnimalShelter<Dog> and AnimalShelter<Cat> are either covariant or contravariant.Covariant and contravariant… in what? They are two different classes. There is no property of any language I know that would guarantee any type relationship between two instances of a parameterized type. C++ is kind of at the extreme where the arguments are little more than macros.
If you’re going to go that far, you might as well go for full delegation. Forwarding makes code unreadable, and I guess delegation does, too.
On 14 May 2016, at 16:41, Egon Elbre <egon...@gmail.com> wrote:By implementing an interface, tagging or constraints.
On 14 May 2016, at 16:41, Egon Elbre <egon...@gmail.com> wrote:
However I do not know how to have the same behavior with a type parameter.
On 14 May 2016, at 16:49, Andreas Söderlund <cisc...@gmail.com> wrote:And as Cope mentioned, there's no real difference in using "addCat" instead of "add", except that the structure-loving nerd inside us will feel hurt, of course. :) We don't want to put animals in a CatShelter, we want to put cats there.
On 14 May 2016, at 16:50, Egon Elbre <egon...@gmail.com> wrote:Based on my experience code with delegation
On 14 May 2016, at 16:41, Egon Elbre <egon...@gmail.com> wrote:By implementing an interface, tagging or constraints.This was the original issue I wanted discussed. So you would rather express the relationship between classes through a common interface, or through tagging, or constraints? Do we have a good analysis of why?
On 14 May 2016, at 17:00, Egon Elbre <egon...@gmail.com> wrote:Implicit interfaces has grown on me. There are potential errors that can happen with them, however in practice happen rarely. Very light-weight on the syntax. Also allows to properly keep the interface and the type at a distance when necessary reducing the amount of dependencies.Explicit interfaces are verbose version. I dislike the hard dependency on the interface -- i.e. means that you sometimes have to include code that isn't being used.
On 14 May 2016, at 17:00, Egon Elbre <egon...@gmail.com> wrote:Implicit interfaces has grown on me. There are potential errors that can happen with them, however in practice happen rarely. Very light-weight on the syntax. Also allows to properly keep the interface and the type at a distance when necessary reducing the amount of dependencies.Explicit interfaces are verbose version. I dislike the hard dependency on the interface -- i.e. means that you sometimes have to include code that isn't being used.Early research by Perry shows that interfaces and a strong type system correlate to low bug density.
On 14 May 2016, at 17:10, Egon Elbre <egon...@gmail.com> wrote:Aah, by implicit interfaces I mean the approach that Go uses... (https://play.golang.org/p/dReJhXKt9b press run to see the error message)
I fail to see your problem. There are 5 roles in BB4bPlan, 2 of
them with methods. (The frontloading algorithm in the big planning
example is similar and well separated from the rest of the
program.)
The ACTIVITY role communicates with its predecessors in its only role methods:
frontloadFrom: projectStartWeek
| maxPred |
self earlyStart: projectStartWeek.
maxPred := PREDECESSORS detectMax: [:pred | pred earlyFinish].
maxPred ifNotNil: [self earlyStart: maxPred earlyFinish + 1].
Many people hate the syntax, but I fail to see that the algorithm itself can be confusing.
The single MODEL role method is equally simple
frontloadFrom: projectStartWeek
ALLACTIVITIES do: [:act | act resetForFrontload].
[CONTEXT remap. ACTIVITY notNil]
whileTrue:
[ACTIVITY frontloadFrom: projectStartWeek].
The CONTEXT remap method for the ACTIVITY role controls the loop:
ACTIVITY
^data allActivities
detect:
[:act |
act earlyStart isNil
and:
[(data predecessorsOf: act) noneSatisfy: [:pred | pred earlyFinish isNil]]]
ifNone: [nil]
Where's your problem?
There are, of course, no interface specifications in Squeak; we
rely on understandable method names and trust that they don't lie.
AlanKay: "Questions of concrete representation can
thus be postponed almost indefinitely because we are mainly
concerned that the computers behave appropriately, and are
interested in particular strategies only if the results are off
or come back too slowly."
So there are naming issues again, and a need to forward fields... what's the real problem here, overloading and overriding? In an attempt to put things in the right ballpark, how about disallowing them, if removing inheritance altogether is too much? Trygve issues a warning for that case for Roles, so it shouldn't be that far off...?
And maybe contravariant parameter types could be allowed too (but this already starts to make the code a bit harder to follow, so maybe not):
class FelineExhibit {
public void add(Animal a) {}
}
I think overriding with the same type should definitely be allowed, otherwise it would be rather limiting:
class FelineExhibit {
public void add(Feline f) {}
}
class CatExhibit extends FelineExhibit {
public void add(Feline f) {}
}
But if we can find a good alternative to classes and inheritance that doesn't introduce new problems, I'm all for it.
Cope, I continue this discussion because you seem to build some
of the Trygve syntax on your bad experience with
myfrontloading code. This experience leads to what I think is an
overly bureaucratic Trygve.
I have searched all users of size and can't find any related to frontloading in the small example, BB4bPlan, or in the big one, BB9Planning. You have apparently got lost in some source code that is the size of a small book; it must be BB9Planning. Here, the frontloading algorithm is specified with 64 LOC, see below. Comments are shown in blue, source code in black.
-------------------------------------------------------
projection: Context-Model
Class: FrontloadCtx
Context subclass: #FrontloadCtx
instanceVariableNames: 'activity model'
category: 'Planning-Context-Model'" Find earliest time period for each activity. "
There are 5 roles that are connected as shown. 2 are shown with heavy outline because they are methodful. The interaction starts in MODEL, this is indicated with a green shadow.
instanceMethods: role binding
remap
" Need to find an activity that is ready for planning first because it is used by two role mappings. "
activity := model allActivities
detect:
[:act |
act earlyStart isNil and:
[(model predecessorsOf: act) noneSatisfy: [:pred | pred earlyFinish isNil]]]
ifNone: [nil].
super remap.ACTIVITY
^activityCONTEXT
^selfMODEL
^modelPREDECESSORS
^activity
ifNil: [Array new]
ifNotNil: [model predecessorsOf: activity]PROJECTSTART
^model projectStartinstanceMethods: operations
frontload: mod
model := mod.
self triggerInteractionFrom: #MODEL with: #frontload.roleMethods: MODEL.
frontload
MODEL allActivities do: [:act | act earlyStart: nil]. " set to unplanned "
[CONTEXT remap. ACTIVITY notNil] whileTrue: [ACTIVITY frontload].roleMethods: ACTIVITY
frontload
| maxPred |
maxPred := PREDECESSORS detectMax: [:pred | pred earlyFinish].
maxPred
ifNil: [ACTIVITY earlyStart: PROJECTSTART.]
ifNotNil: [ACTIVITY earlyStart: maxPred earlyFinish + 1].
-------------------------------------------------
That's all. There is, of course, the problem that this code is as
unreadable to most people as C++ is to me. This makes it hard for
them to form a reasoned opinion. But that doesn't mean that it is
unreadable to someone who is familiar with the language. I suspect
you got lost in the part of the class library that is part of the
ST language. In your Trygve write-up you describe the
for-statement:
for (int aScore : scores) total = total + aScore
In ST, this is written:
scores do: [aScore | total := total + aScore]
This construct is explained in a beginner's guide to ST. A major
difference is that while the first construct is part of the Trygve
language, the second is part of the ST class library. It is,
therefore, possible to look up the code in ST (and
not in Trygve, where you have to use the construct on trust):
Array::do: aBlock
1 to: self size do:
[:index | aBlock value: (self at: index)]
could this be where you found your confusing size? The
language parts of the class library are indistinguishable from the
classes defined by the user. This is unconventional and can
confuse the innocent.
--
On 15 May 2016, at 14:21, Trygve Reenskaug <try...@ifi.uio.no> wrote:Cope, I continue this discussion because you seem to build some of the Trygve syntax on your bad experience with myfrontloading code. This experience leads to what I think is an overly bureaucratic Trygve.
I strongly suspect that if the Squeak implementation had a syntax
for specifying role-object contracts, Cope and others would have
found the code significantly easier to read, since it would have
shown clearly which instance methods were being used in each
Context. The trygve language goes another step beyond that by not
automatically exposing methods in the contract to other roles -
perhaps that's what Trygve is referring to, and I have some doubts
about the necessity of that as well. But if it helps some people
to find the code easier to reason about without getting in the way
for others, then maybe it's not "overly bureaucratic".
On 15 May 2016, at 15:20, Matthew Browne <mbro...@gmail.com> wrote:The trygve language goes another step beyond that by not automatically exposing methods in the contract to other roles - perhaps that's what Trygve is referring to
line 239: NONCOMPLIANT: Trying to enact object script `setPosition(int)' without using the interface of the Role it is playing: `SpellChecker’.I think Trygve would be better without this distinction because it would be simpler: A role would be the name of an object. Full stop. Simplicity is always my goal and it's always a question of balance between power and simplicity. In this case, the two interfaces adds complexity without an indispensable gain, IMO.
class Int {public Int(int i) { i_ = i.clone }public Int(int i, Int otherOne) { i_ = i.clone other = otherOne }public void incr() { i_ = i_ + 1; if (other != null) other.incr }public void print() const { System.out.println(i_) }int i_;Int other}context UseCase {role I1 {public void step() { incr() }public void report() const { this.print() }} requires {void print() const;void incr()}role I2 {public void step() { incr() }public void report() const { this.print() }} requires {void print() const;void incr()}public void trigger(Int i1, Int i2) {I1 = i1I2 = i2I1.stepI1.reportI2.report}}{Int i2 = new Int(3);Int i1 = new Int(4, i2);new UseCase().trigger(i1, i2)}
class Int {public Int(int i) { i_ = i.clone }public void incr() { i_ = i_ + 1}public void print() const { System.out.println(i_) }int i_;}context UseCase {role I1 {public void step() { incr(); I2.incr() }public void report() const { this.print() }} requires {void print() const;void incr()}role I2 {public void step() { incr() }public void report() const { this.print() }} requires {void print() const;void incr()}public void trigger(Int i1, Int i2) {I1 = i1I2 = i2I1.stepI1.reportI2.report}}{Int i2 = new Int(3);Int i1 = new Int(4);new UseCase().trigger(i1, i2)}
Example: Frontloading. It is essential that all activities are in a known state before the computation of the activities' earlyStart starts. Hence the very first expression in the interaction algorithm:
ST:
PLAN allActivities do: [:act | act earlyStart: nil]. "
set to unplanned "
Trygve:
for (Activity act : model.allActivities())
act.setEarlyStart(void) // set to
unplanned
Original Java etc:
Activity[] allActivities = model.allActivities();
for (int i = 0; i <
allActivities.size(); i++) {
allActivities[i].setEarlyStart(void) //
set to unplanned
}
On 16 May 2016, at 01:47, Matthew Browne <mbro...@gmail.com> wrote:
On 5/15/16 10:03 AM, James O Coplien wrote:It’s important — from the perspective of being able to reason about the code — that only the Role be able to change its Role-player’s state. Without that restriction it is impossible to reason about object state from a Role,"Impossible" is a pretty strong word to use.
and without that it is impossible to reason about system state of a Context.
All the methods that can be called anywhere in the Context are listed in either the role-object contract (in the case of instance methods) or in the role itself (in the case of role methods). So while it may be true that it's harder to reason about the state of a role player when you have to look outside the role to do so, you certainly shouldn't have to look outside the Context to do so. So I don't think your second assertion follows from the first. I don't see how promoting an instance method to a role method (as was recently introduced to trygve) or writing your own forward…
On 08 May 2016, at 10:52, Trygve Reenskaug <try...@ifi.uio.no> wrote:Here's an idea. Gain: the complete role interface distinguishing which parts are visible to the environment. Lose: The explicit role/object interface.
On 15 May 2016, at 17:44, Trygve Reenskaug <try...@ifi.uio.no> wrote:I think Trygve would be better without this distinction because it would be simpler: A role would be the name of an object. Full stop.
context Arena implements EventHandler {public Arena() {MyPanel panel = new MyPanel();THE_PANEL = panel;THE_PANEL.clear();BALL = new BallObject(50, 50);PADDLE = new Point(450, 560);panel.setEventHandler(this)}public void run() {do {THE_PANEL.clear();PADDLE.draw();BALL.draw()BALL.velocityAdjust();BALL.step();THE_PANEL.flush();Thread.sleep(20)} while (true)}role THE_PANEL {} requires {void drawCircle(int x, int y, int r);void drawPADDLE(int xs, int ys, int h, int w);int maxX() const;int maxY() const;void flush();void clear();void fillOval(int x, int y, int h, int w);void drawRect(int x, int y, int h, int w);void fillRect(int x, int y, int h, int w);int xsize() const;int ysize() const;void setColor(Color color);void repaint();}role PADDLE {} requires {int thickness() const;int width() const;void draw();void erase();void moveTo(Point p),boolean contains(int x);int vertical() const { return y() - thickness() }void moveTo(Point p);boolean contains(int x)int vertical() const;void setXY(int x, int y);int x() const;int y() const}role BALL {} requires {void draw();void erase();void step();void velocityAdjust()boolean bouncingOffOfPADDLE()void setXY(int x, int y);int x() const;int y() const;Point velocity();void setVelocity(Point velocity);int radius()}private boolean handleEvent(Event e) {if (e.id == Event.MOUSE_MOVE) {PADDLE.moveTo(new Point(e.x, e.y))}return true}}new Arena().run()
On 16 May 2016, at 01:47, Matthew Browne <mbro...@gmail.com> wrote:
Roles can still affect the state of other role players; it's just a question of whether they have to go through the gateway of the role interface in order to do so. I think the 'const' modifier already helps a good bit in this area.
On 16 May 2016, at 01:47, Matthew Browne <mbro...@gmail.com> wrote:
The other thing that bothers me a bit about prohibiting direct access to instance methods from other roles is that it maintains what I see as essentially a compile-time separation at run-time.
On 16 May 2016, at 01:47, Matthew Browne <mbro...@gmail.com> wrote:
So while it may be true that it's harder to reason about the state of a role player when you have to look outside the role to do so, you certainly shouldn't have to look outside the Context to do so.
context Context {role auditor {public void audit() {// some auditing code}}role list {public void add(String item) { atPut(incrementIndex(), item) }public String getAt(int theIndex) {String retval = at(translateIndex(theIndex));return at(translateIndex(theIndex))}private int translateIndex(int i) { return i + 1 }private int incrementIndex() {String theIndex = at(0);int rawIndex = 1;if (theIndex is null) {atPut(0, "1")} else {rawIndex = theIndex.toInteger() + 1;atPut(0, rawIndex.toString())}return rawIndex}public int size() // publish} requires {int size();String at(int theIndex);void atPut(int theIndex, String object)}public Context() { list = new String [16] }public void trigger() {list.add("123");list.add("456");auditor.audit();String j = list.getAt(1);System.out.format("list element is %s\n", j)}}new Context().trigger
Cope,
That was a very thorough and illuminating post! My response won't
be nearly as thorough, largely because I don't actually have a
strong objection to the instance method access restriction in the
current version of trygve. While obviously I was leaning against
it, my main argument was not that it was necessarily bad - and it
may even be very good as you are arguing - but that there were
factors that deserved further consideration before baking this
restriction into the language for good.
The restriction could still be lifted later of course, but IMO it's better to be quite sure about a restriction before putting it in in the first place. Such certainty requires considering various opinions, researching history and theory, and if possible some empirical evidence based on the experience of more than just one or two programmers (which is tricky since not many people are using DCI in the wild yet, so we're mostly left with extrapolating more or less analogous evidence from other paradigms). Especially given this latest post, I think you have done all of these, although obviously we don't have any real-world evidence about this specific to DCI since trygve is a brand new language. I don't claim to have a strong theoretical backing for the points I was raising, aside from my personal theories about DCI that I have formed during my time as a member of this group and as a DCI programmer (note that in general, my "personal theories" are still in many ways grounded in earlier theory, most of which I've learned through the DCI literature). The purpose of my previous post was to at least raise some questions since I think this deserves further discussion, so I am glad my points have been heard and I think there might be some value in clarifying and commenting further on some things...
As a DCI implementation author, I still hesitate to add a similar
restriction to the JS implementation I am currently focused on,
but you have certainly made a strong case for it and I will be
watching any future discussions about this carefully as I am far
from fully decided on this.
So it went into trygve from the beginning. I think it’s a good thing in its own right. We have often discussed it here on the list. We have talked about making it explicit. I don’t think anyone else has done that (except Matthew in his new language variant). I have.
That is certainly a style of programming, but I wouldn’t call it DCI in terms of the pictures Trygve has drawn, the code he has written, and the discussions we have had here. The following code meets all the criteria people have been asking for but I hardly feel it rises to provide DCI’s benefits. It just makes identifiers into fat first-class administrative entities (talk about bureaucratic!)
This code would compile and run if all the code on the other side of the trusted abstraction boundary were there in the classes. Because it is on the other side of the trusted abstraction boundary, that you are unable to read it shouldn’t make you nervous. It meets all the criteria posited by those advocating direct access of instance methods. Can you analyze the code to understand what it does? Good, if you can, because then we don’t need DCI and we can just go back to class-oriented programming. If you can’t, it just may be the fault of the reasoning that has been presented in recent mails. Point finale.
Yet the Contexts in Matt’s trivial examples (e.g., https://github.com/DCI/scaladci/blob/master/examples/src/test/scala/scaladci/examples/ShoppingCart7.scala)
Hi Quang,
(When I write this it looks almost like I'm
misspelling your name, but I'm just saying hi :) )
Thanks for your post. I just wanted to clarify that what you
discussed here is actually a separate issue from what I was
responding to. I already agree with Cope that role and context
methods should only be allowed to access instance methods that are
defined in the role-object contract. I'm still open to the idea of
declared types for role-object contracts in some cases, but even
in that case I'd still want the compiler to require a duck type as
well so we can easily see at a glance all the instance methods
that will be used in that Context, and so that methods not
listed in the contract cannot be called.
The question I'm concerned about is more specific: once an
instance method has been specified in a role-object contract,
should it be automatically accessible so it can be called from
anywhere in the context, or should only its own role have access
to it by default? I think Trygve posted a concrete example earlier
in this thread that triggers the warning we're discussing.
--
So it went into trygve from the beginning. I think it’s a good thing in its own right. We have often discussed it here on the list. We have talked about making it explicit. I don’t think anyone else has done that (except Matthew in his new language variant). I have.
This is getting too advanced for me and and need some
clarification. I am talking about the visibility of roleplayer
methods as seen from other roles.
If I, in a role script in the frontloading context (PLAN role, frontload() script), write
for (Activity act : model.allActivities())
act.setEarlyStart(void)
where setEarlyStart() is implemented as
instance methods in all classes that can provide activity objects.
I interpret you as saying that I don't know where I am with this
code and might hit the halting problem. Even if I add
requires {... void setEarlyStart(int start) ... }
the danger remains. But if I extend the ACTIVITY role with
the declaration
public void setEarlyStert(int start)
the I do know where I am. The halting problem has
gone and everything is fine. My conclusion is that I have clearly
misunderstood your arguments, so I would appreciate a
clarification.
--
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 post to this group, send email to object-co...@googlegroups.com.
Visit this group at https://groups.google.com/group/object-composition.
For more options, visit https://groups.google.com/d/optout.
The essence of object orientation is
that objects collaborate to achieve a
goal.
Trygve Reenskaug mailto: try...@ifi.uio.no
Morgedalsvn.
5A http://folk.uio.no/trygver/
N-0378
Oslo http://fullOO.info
Norway Tel:
(+47) 22 49 57 27
On 18 May 2016, at 06:11, Hai Quang Kim <wan...@gmail.com> wrote:So I just want to suggest new syntax:role THE_PANEL : Panel{role methods()}[no require, I can use all object methods]In ContextTHE_PANEL.role methods()THE_PANEL.object methods()
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:I agree that the code example you posted was difficult to understand because it relied way too much on delegating the use case behavior to instance methods, making the Context abstract rather than compressed.
On 18 May 2016, at 08:07, Rune Funch Søltoft <funchs...@gmail.com> wrote:It has been part of Marvin from day one as well. I originally invented it to make the compiler implementation easier but found that it help communicate intend. I also tried out to other variations. One where the compiler relies on the DLR to do run time type checks and method lookups and one where you type the role. In general I prefer the role contract (requires) but found that using the typed version was a great help when integrating towards existing code
On 18 May 2016, at 11:24, Trygve Reenskaug <try...@ifi.uio.no> wrote:My conclusion is that I have clearly misunderstood your arguments, so I would appreciate a clarification.
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:
But allowing an instance method to be accessed by other roles would still allow programmers to put behavior in the role methods and interact with the role via its role interface rather than its 'requires' interface as much as they deemed appropriate.
So the rationale for the current restriction in trygve needs to be stronger than that.
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:
How does promoting the instance method to a role method ensure the integrity of the role player any better than calling the changeUsername instance method directly?
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:
But things become less clear-cut when you consider that we're talking about public instance methods here (because if they weren't public, then the role wouldn't be able to call them in the first place).
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:
Outside of any Context, the programmer can write code to mutate the state of the object via public instance methods in any sequence that they please and anywhere in the code base. That's a giant loophole. If the instance methods of an object should only be accessible by a certain set of roles, then that loophole needs to be closed.
Maybe the DCI paradigm calls for some new access levels or rules that better takes role-binding into account. On the other hand, maybe it should just be entirely up to the object's class to protect the object's integrity when its public methods are called. I'm not sure which approach is better. Hopefully we can get more concrete about that as we continue this discussion, and I agree that more concrete discussions are often far more productive.
--Which long mail? Date and time please.
With the trygve approach you can choose to expose the instance method interface in the Role interface, or to control access to the instance methods through the Role.
With your suggestion you are forced to expose the instance method interface as part of the Role interface, and cannot code the Role to regain control of access to the object.
Which one is more reminiscent of a bureaucracy? I give options. Yours forces what is commonly recognized as violation of encapsulation.
So the rationale for the current restriction in trygve needs to be stronger than that.
No, it doesn’t. What is weak about the above rationale? Seems pretty compelling to me — unless code comprehensibility is held to be irrelevant.
On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:
How does promoting the instance method to a role method ensure the integrity of the role player any better than calling the changeUsername instance method directly?
It doesn’t. It just makes it visible, through a suitable, distinguished mechanism, that in your ego you have screamed “I KNOW BETTER THAN THE COMPILER!” and that you have taken conscious effort to declare that and to tell the compiler to go away. It’s just a way of shutting up the compiler, but keeping all readers on notice that the Role is a leaky abstraction.
I would have no argument at all against more precise access restrictions for instance methods if instance methods in the requires {} section were public rather than private to the surrounding context by default. It's the assumption that they should generally be private that I disagree with. In any case I think the ability to make an instance method private to a given role would still be useful, e.g.:
role Customer {
...
private void changePassword(String password);
}
requires {
void changePassword(String password);
}
Or maybe:
role Customer {
...
}
requires {
local void changePassword(String password);
}
(It would have to be an alternative word like "local" with this
approach because "private" already has a meaning; if I understand
correctly, "private" would mean that the role player's class
should have a private method called
changePassword...although I don't know why a Context programmer
would care what private methods a role player had.)
On 18 May 2016, at 14:49, Matthew Browne <mbro...@gmail.com> wrote:This leads me to believe that the word "bureaucratic" is too ambiguous for this discussion.
On 18 May 2016, at 15:31, Matthew Browne <mbro...@gmail.com> wrote:I would have no argument at all against more precise access restrictions for instance methods if instance methods in the requires {} section were public rather than private to the surrounding context by default. It's the assumption that they should generally be private that I disagree with. In any case I think the ability to make an instance method private to a given role would still be useful, e.g.:
On 18 May 2016, at 17:09, Hai Quang Kim <wan...@gmail.com> wrote:eg.Role WINDOW: Panel
{void myRoleMethod() {self.DataObjectMethods() // public methods only, I am not sure about calling private methods}// no require section needed}
On 18 May 2016, at 18:08, Matthew Browne <mbro...@gmail.com> wrote:From that particular mail, the only potential action item is adding "Warning: " to the beginning of all warning messages. I can submit a pull request for that if you're open to it.
My more serious proposal is in the next mail I sent,
I appreciate Dr. Cope's push to core matters!!!I also appreciate Matthew's attention to UX detail re: the warning message :-)
On 18 May 2016, at 20:19, Rune Funch Søltoft <funchs...@gmail.com> wrote:I'd say that at least a substantial part of the comprehendability problem would be gone with an immutable approach and you all know I think that's where we should gone.
Rune,
In your vision, would all domain objects (or even all objects
generally?) be immutable, or only those that are pure data? I
remember you previously pointed out the distinction between state
and data and you said you still thought there was a place for
domain objects with mutable properties, but I'm not clear whether
or not that was only in reference to non-FP languages.
On 18 May 2016, at 20:19, Rune Funch Søltoft <funchs...@gmail.com> wrote:
All that said I think the requires section should be private. With no way of changing that to public.
On 18 May 2016, at 20:24, Raoul Duke <rao...@gmail.com> wrote:I do wish somebody smart had time and energy to take a pure fp / applicative / whatever you feel is opposite mutable OO as possible approach to see how it would work out. :-)
On 18 May 2016, at 21:30, Matthew Browne <mbro...@gmail.com> wrote:Encapsulation has historically been about encapsulation within the object boundary.
And I fail to see how the absence of any way of promoting an instance method to a role method would be a good thing...as we have seen, it frequently leads to awkwardly-named methods that do nothing more than forward to the instance methods
And I fail to see how the absence of any way of promoting an instance method to a role method would be a good thing...as we have seen, it frequently leads to awkwardly-named methods that do nothing more than forward to the instance methods
I am totally lost about the antecedent to this argument, because trygve currently allows just such a promotion to avoid the awkward re-naming. Is this another ghost-non-problem? What is your point?
All that said I think the requires section should be private. With no way of changing that to public. If there should be an exception if should be to allow const methods to be exposed.
On 18 May 2016, at 22:59, Matthew Browne <mbro...@gmail.com> wrote:I think that forbidding all or all non-const instance methods from being promoted to role methods would be going too far.
On 18 May 2016, at 22:59, Matthew Browne <mbro...@gmail.com> wrote:
I think that forbidding all or all non-const instance methods from being promoted to role methods would be going too far.
Why?
private Panel panel_; private Frame frame_; private Mouse mouse_;
role Display requires Display; role World requires World;
role Handler { public void collide(Entity A, Entity B){ onCollision(A, B); } } requires { public void onCollision(Entity A, Entity B); }
role Handler requires CollisionHandler"
And I fail to see how the absence of any way of promoting an instance method to a role method would be a good thing...as we have seen, it frequently leads to awkwardly-named methods that do nothing more than forward to the instance methods.
Rune,
In your vision, would all domain objects (or even all objects generally?) be immutable, or only those that are pure data? I remember you previously pointed out the distinction between state and data and you said you still thought there was a place for domain objects with mutable properties, but I'm not clear whether or not that was only in reference to non-FP languages.
On 19 May 2016, at 08:28, Hai Quang Kim <wan...@gmail.com> wrote:In this case, we must be careful when drawing a link not only from Role to Role but also from Role to Data Object too.
Btw, I brought this up because I am not the only one think it that way. (no offense here)
That why I bring this up, just in case we need more input for next move.
It looks like the main upshot of this plaint was that the poster wants to be able to substitute an interface name for the duck-typed list of signatures. I am considering that, but am very careful about mixing Role and class worlds in any way. Interfaces are a way of organising classes; Roles, a way of organising objects. The current duck typing makes the object perspective clear; using interfaces may cause people to revert to class thinking.
Take it this way if the best name you can come up with for a role method is the same as the name of a required instance method then one of two are likely true
1) It belongs to the role2) it belongs to the role player and not the role
One hidden agenda I have with trygve is to get DCI into adaptation land (and it’s working — there have been over 100 DCI Tweets on Twitter in the past 48 hours, that I know of) and that entails a “worse is better” approach (also working).
On 18 May 2016, at 14:23, Trygve Reenskaug <try...@ifi.uio.no> wrote:--Which long mail? Date and time please.
context Context {
role auditor {
public void audit() {
//list.atPut(2, "789")
}
}
role list {
public void add(String item) { atPut(incrementIndex(), item) }
public String getAt(int theIndex) {
String retval = at(translateIndex(theIndex));
return at(translateIndex(theIndex))
}
private int translateIndex(int i) { return i + 1 }
private int incrementIndex() {
String theIndex = if (size() > 0) at(0) else null;
int rawIndex = 1;
if (theIndex is null) {
atPut(0, "1")
} else {
rawIndex = theIndex.toInteger() + 1;
atPut(0, rawIndex.toString())
}
return rawIndex
}
public int size()
} requires {
int size() const;
String at(int theIndex) const;
void atPut(int theIndex, String object)
}
role Test {
public void access() {
String k = list.at(0);
System.out.println(k)
}
}
public Context() { list = new String [16] }
public void trigger() {
Test.access();
list.add("123");
list.add("456");
auditor.audit();
String j = list.getAt(1);
System.out.format("list element is %s\n", j)
}
}
Consider:On 18 May 2016, at 06:44, Matthew Browne <mbro...@gmail.com> wrote:
Outside of any Context, the programmer can write code to mutate the state of the object via public instance methods in any sequence that they please and anywhere in the code base. That's a giant loophole. If the instance methods of an object should only be accessible by a certain set of roles, then that loophole needs to be closed.
Maybe the DCI paradigm calls for some new access levels or rules that better takes role-binding into account. On the other hand, maybe it should just be entirely up to the object's class to protect the object's integrity when its public methods are called. I'm not sure which approach is better. Hopefully we can get more concrete about that as we continue this discussion, and I agree that more concrete discussions are often far more productive.
And that is why I brought up the iAPX432, which indeed provided facilities for doing that, and implemented them in an Ada compiler. And it was a facility provided by the C++/P language we did back in 1983 or so.
But it’s irrelevant. This is about code comprehensibility rather than formal proof of correctness. From the standpoint of formal correctness your argument could be used against any and all features that promote encapsulation. I think that both confuses the issue, and is not productive.
Consider the following example:
context GreetCustomer {
public GreetCustomer(User customer) {
Greeter = this;
Customer = customer;
}
public void greet() {
Greeter.greet();
}
stageprop Greeter {
public void greet() {
System.out.println("Welcome, " + Customer.firstName() + "!");
}
}
stageprop Customer {} requires {
public String firstName() const
}
}
class User {
public String firstName() const {return firstName_}
private String firstName_;
...
}
This is a very trivial example and of course we wouldn't even need a Context for something like this, let alone two different roles.
But I think it shows how it's quite natural and readable to call another role player's instance method directly.
If you're limited to using only Customer's role interface and instance methods can't be promoted to role methods (which seems to be what you're advocating, although this is perhaps an example of where you'd make an exception since you said it might be OK for const methods), then I guess you'd have to do something like this:
stageprop Greeter {
public void greet() {
System.out.println("Welcome, " + Customer.getFirstName() + "!");
}
}
stageprop Customer {
public String getFirstName() {return firstName()}
}
requires {
public String firstName() const
}
On 19 May 2016, at 15:58, Matthew Browne <mbro...@gmail.com> wrote:How is the ability to call changePassword() directly from outside the Context (or without addressing it via a role) not a loophole in your claimed encapsulation?
On 19 May 2016, at 15:58, Matthew Browne <mbro...@gmail.com> wrote:
How is the ability to call changePassword() directly from outside the Context (or without addressing it via a role) not a loophole in your claimed encapsulation?
Matthew,
You have posed much the same question before. At the time I explained in great detail why the question is irrelevant.
On 18 May 2016, at 19:37, Raoul Duke <rao...@gmail.com> wrote:I also appreciate Matthew's attention to UX detail re: the warning message :-)
On 18 May 2016, at 14:49, Matthew Browne <mbro...@gmail.com> wrote:I actually didn't realize it was only a warning until you pointed it out, because warnings look the same as errors (perhaps the code I was writing the last time I encountered this caused an actual compile error at the same time, or maybe I just didn't notice that the "Run" button had become active).
I'm interested to hear Cope's response to this. But I also just wanted to say that personally I'm fine with Cope's proposed solution of printing the number of error messages at the bottom. If at the bottom it had said:
1 warning
0 errors
...then I doubt I would have failed to notice that it was just a warning and not an error.
Thanks Raoul and Cope,
Matt
On 19 May 2016, at 22:38, Matthew Browne <mbro...@gmail.com> wrote:I'm interested to hear Cope's response to this. But I also just wanted to say that personally I'm fine with Cope's proposed solution of printing the number of error messages at the bottom. If at the bottom it had said:
1 warning
0 errors...then I doubt I would have failed to notice that it was just a warning and not an error.
Thanks Raoul and Cope,