Hello Richard,

> What part of … is hard to understand?

Did you mean for this to come off as condescending? Or are you are honestly 
wondering how effectively you are communicating? In either case, all of it is 
hard for me to understand since it doesn’t describe the Proxy Pattern which 
requires that "Clients can't tell whether they work with a subject or its 
proxy” (https://en.wikipedia.org/wiki/Proxy_pattern 
<https://en.wikipedia.org/wiki/Proxy_pattern>). My understanding of a proxy is 
that it will rarely/never happen that "there will be *more* selectors you 
needed to think about but didn’t." So, far from being (an instance of) the 
problem, an override of #’doesNotUnderstand:’ is the elegant solution.

> I wanted something that was like an OrderedCollection but could only hold 
> elements of a specific class.

It seems what you want is not a Proxy but a Facade 
(https://en.wikipedia.org/wiki/Facade_pattern 
<https://en.wikipedia.org/wiki/Facade_pattern>) in which you "may perform 
additional functionality [such as type checking] before ... forwarding a 
request” (https://en.wikipedia.org/wiki/Facade_pattern 
<https://en.wikipedia.org/wiki/Facade_pattern>). 

> I *could* make a proxy by going through #doesNotUnderstand: but I am not 
> going to.

That’s fine, and a proxy is probably inappropriate for the things you do. But 
there are a few usage scenarios 
<https://en.wikipedia.org/wiki/Proxy_pattern#Possible_usage_scenarios> where 
they are appropriate. My primary experience with them is with distributed 
object communication 
<https://en.wikipedia.org/wiki/Distributed_object_communication> where they are 
appropriate.

> I *could* make a batch of ricin and store it in my granddaughter's bedroom, 
> but I'm not going to.

I’m glad to hear it. To take a more realistic example, I could try to fly a 747 
but I’m not going to (since my commercial pilot’s license doesn’t include that 
rating). That doesn’t mean that no one should fly a 747 (or implement a 
Proxy—the people who make GemBuilder for Smalltalk 
<https://gemtalksystems.com/products/gbs-vw/> should certainly use proxies). I 
remember hearing a joke about Kent Beck holding a sign saying “Will override 
#doesNotUnderstand: for food”! I took it as not saying that only a genius can 
understand DNU but that you should think carefully about how it will be used 
(and in many cases something else would be appropriate). Everything else about 
Kent Beck’s work makes it clear that leaving behind clear maintainable code is 
a top priority.

James

> On Mar 19, 2022, at 11:11 PM, Richard O'Keefe <rao...@gmail.com> wrote:
> 
> An override of #doesNotUnderstand: *is* (an instance of) the problem.
> What part of "you may think you know what to forward now, but just
> wait a couple of months or install an additional package and there
> will be *more* selectors you needed to think about but didn't" is
> hard to understand?
> 
> Proxies done right are proxies *relative to a specified protocol*.
> 
> Let me give you a personal example.
> I wanted something that was like an OrderedCollection but
> could only hold elements of a specific class.
> No worries, just make a proxy.  Make a bare-bones object,
> define all the #add... methods to check their argument,
> and use #doesNotUnderstand to forward everything else to
> the underlying OrderedCollection.
> 
> The first problem is the selectors that the bare-bones
> object *does* understand by virtue of being an object.
> (Remember there are *hundreds* of these selectors in
> Squeak, Pharo, VisualWorks, even in gst there are more
> than 120).  You must, for example, ensure that #instVarAt:
> is forwarded, not handled locally, BUT you must also
> ensure that #instVarAt:put: is handled locally, not forwarded.
> And then one day you find that your program has broken,
> because mutable collections now have
>   aMutableCollection inPlaceCollect: collectBlock
> and that can put unacceptable results in the collection
> *without* going through any #add... method.  
> 
> Eventually you realise "handing out a wrapper for this
> collection constrained to only allow adding certain things"
> is the wrong way to do it, and you do something like
> 
>     constrainedAdder: constraint
>       ^[:x | (constraint value: x)
>                ifTrue:  [self add: x]
>                ifFalse: [DomainError receiver: self selector: #add:]]
> 
> or you do something else entirely, like handing out a wrapper with
> a *narrow* interface that doesn't make the slightest pretence of
> *being* the other object.
> 
> I *could* make a batch of ricin and store it in my
> granddaughter's bedroom, but I'm not going to.
> I *could* make a proxy by going through #doesNotUnderstand:
> but I am not going to.  That would be a textbook example of
> maxim 38: "Just because it's easy for you doesn't mean it
> can't be hard on your clients."
> 
> 
> 
> 
> 
> 
> On Sun, 20 Mar 2022 at 15:18, James Foster <smallt...@jgfoster.net 
> <mailto:smallt...@jgfoster.net>> wrote:
> I don’t understand. Wouldn’t an override of #'doesNotUnderstand:’ solve this 
> problem? The proxies I’ve seen subclass from nil or ProtoObject and forward 
> almost everything to the target. It’s really very easy.
> 
>> On Mar 19, 2022, at 3:14 AM, Richard O'Keefe <rao...@gmail.com 
>> <mailto:rao...@gmail.com>> wrote:
>> 
>> An object should be a Proxy or Stub only with reference to a specific 
>> protocol, which should be kept narrow.
>> 
>> Being a Proxy is a form of coupling.  Proxying a wide
>> interface creates maintenance problems:
>> Squeak 5.3 : Object selectors size => 485
>> Pharo 9.0  : Object selectors size => 435
>> astc       : Object selectors size => 325
>> VW 8.3PUL  : Object selectors size => 304
>> 
>> The interface of Object is HUGE.  You want to bet that
>> your Proxy got *all* of the methods right?  This
>> interface didn't get that way all at once; it grew.
>> The number was 78 in Smalltalk-80.  At a minimum, then,
>> Smalltalk systems have accreted one extra Object method
>> every two months.
>> 
>> So you set up your proxy to *flawlesly* mirror Object,
>> and then, WHOOPS, upgrade Smalltalk and now there is a
>> method that Object has and your Proxy either lacks (if
>> it descends from ProtoObject but not Object) or inherits
>> an inappropriate version of (if it descends from Object).
>> 
>> What this means is that nobody ever *does* flawlessly
>> mock everything in the public interface of an object
>> they are Proxying.  They proxy a *limited* protocol.
>> Because that is all they *can* do.
>> 
>> Look, I know that people who have been trained to work
>> with the stuff can use C4 as cooking fuel.  But I haven't
>> had that training, so I won't touch the stuff.  In the
>> same way, I dare say there are things *you* can safely
>> do in Smalltalk that fumblefingers here would be burnt
>> badly by.  There are many things that *can* be done that
>> I *won't* do.  In a chemistry lab, I would not work with
>> ClF3 let alone O2F2.  In Smalltalk, I don't monkey with
>> #isNil.
>> 
>> On Fri, 18 Mar 2022 at 03:52, James Foster <smallt...@jgfoster.net 
>> <mailto:smallt...@jgfoster.net>> wrote:
>> Richard,
>> 
>> I very much admire Dijkstra’s admonition regarding “The Humble Programmer” 
>> and was pointing a student to that article just this week. 
>> 
>> In any case, I think you’ve demonstrated that you now comprehend the 
>> argument against inlining—you just don’t agree. That’s fair and I think the 
>> discussion has been clarified. Would it be fair to say that you have an 
>> “ideological objection” to allowing a Proxy or Stub to transparently stand 
>> in for another object (say, in a two-object-space environment such as Pharo 
>> and GemStone)? That is, a domain object can’t be replaced by a Proxy or Stub 
>> without a wholesale rewrite of the rest of the application? I respect that 
>> as a reasonable position (demanding perfect clarity), but I see a cost to 
>> that position as well.
>> 
>> Of course, if you really want to avoid allowing the receiver to chose its 
>> response to a message, you can use other messages. So if you want to find 
>> out if an object is identical to nil you should use `nil == myObject` to 
>> ensure that there was not an override of #’isNil’ or #’==‘ by the object’s 
>> class. 
>> 
>> James
>> 
>>> On Mar 17, 2022, at 2:27 AM, Richard O'Keefe <rao...@gmail.com 
>>> <mailto:rao...@gmail.com>> wrote:
>>> 
>>> My chief concern is that I am a bear of very little brain,
>>> and if you change the meaning of #isNil to anything at all
>>> other than "is the receiver identical to nil" you *WILL*
>>> (not may) confuse me.  This extends to things that happen
>>> not to be inlined: if even a god-like Smalltalker like
>>> Andres Valloud overloads #, to something other than "combine
>>> the collection that is the receiver with the collection that
>>> is the argument to yield a new collection" than I *WILL*
>>> most certainly be confused and find the code unmaintainable.
>>> Smalltalk being Smalltalk, if you admit an inconsistent
>>> overload anywhere, I can no longer understand sends of that
>>> selector anywhere.  One of the things to like about Traits
>>> is that you can say "this class doesn't just *happen* to
>>> have selectors x and y, it has them *because* it has this
>>> whole consistent bundle of selectors."
>>> 
>>> There are more annotations documented for my Smalltalk
>>> compiler than are actually implemented.  One that *is*
>>> implemented is <doNotOverride>, and it has caught more
>>> mistakes than I care to admit to.  It's particularly
>>> important for a bundle of methods with varying arguments
>>> that are meant to be routed through a single method,
>>> which *is* meant to be overridden.  It makes sure that
>>> I override the *right* method.  (Take #= and #~= as an
>>> obvious example.)
>>> 
>>> Once you start down the path of lying about things like #isNil
>>> you find that EITHER you have to go very far down that path
>>> and override #== and #instVarAt: and a whole lot of other
>>> things OR you are working with a semantically incoherent system.
>>> 
>>> "How should a proxy (https://en.wikipedia.org/wiki/Proxy_pattern 
>>> <https://en.wikipedia.org/wiki/Proxy_pattern>) to nil respond to the 
>>> #’isNil’ message?"
>>> 
>>> It SHOULD NOT LIE.  A proxy *isn't* nil, and it doesn't *behave* like nil,
>>> even if it is a proxy for nil.  A proxy, qua proxy, can do things that nil
>>> cannot.  Use another selector, #isEffectivelyNil, or whatever reveals your
>>> intentions, and give it what semantics you find useful.
>>> 
>>> "How should the Null Object Pattern 
>>> (https://en.wikipedia.org/wiki/Null_object_pattern 
>>> <https://en.wikipedia.org/wiki/Null_object_pattern>) respond to #’isNil’?"
>>> 
>>> It should answer false.  Period.  No ifs, buts, quibbles, or maybes.
>>> The whole *point* of the Null Object Pattern is to return something
>>> that *isn't* nil, that has quite a different protocol.  If you call
>>> something that is supposed to return either a Foo or a NullFoo, and
>>> it gives you nil instead, there is a BUG in what you just called so
>>> the sooner you find out the better.  What you want is
>>> 
>>>    Foo >> isNullFoo ^false
>>>    NullFoo >> isNullFoo ^true
>>> and no other class defines #isNullFoo.
>>> 
>>> That way, when you ask "tripeWorks grobblingMill lastPallet isNullPallet"
>>> (a) you make it clear to someone reading your code what you are expecting
>>> (b) if you DON'T get what you are expecting, Smalltalk will tell you.
>>> 
>>> I must admit that on the few occasions when I've used Null Object
>>> I've used an all-purpose #isMissing instead of a task-appropriate
>>> #isNullFoo, but then I figured out what I was doing wrong.  You
>>> look at the code, you say "there's *something* off here, but I don't
>>> know what."  But when you ask "what, exactly, does this method MEAN?"
>>> you realise "oh, THAT'S what I was doing wrong."  #isMissing told me
>>> it was a NullPerson or a NullAddress or a NullPartsList or ... but
>>> in this case I needed to know whether it was a NullSummary.
>>> 
>>> And of course you run into E.F.Codd's lesson: "one NULL is never
>>> enough, information can be missing for more than one reason".
>>> Take the familiar example of a phone number:
>>>  - I know that Fred's phone number is X
>>>  - I know that Fred has a phone but I don't know what the number is
>>>  - I don't know whether Fred has a phone or not
>>>  - I know that Fred has no phone
>>> There's room for three *different* null objects there.
>>> Should we have UnknownNumberOfActualPhone to answer false to #isnil
>>> and NonNumberOfNonexistentPhone to answer true?  Or what?
>>> 
>>> By the way, you may have misunderstood my benchmark.
>>> It wasn't that #isNil or even _ ifNotNil: speeded up by
>>> 10%, it was the *whole* matrix-munching benchmark that
>>> speeded up.  Certainly not a big deal, but it's not
>>> something I'd be happy to give up in order to get less
>>> maintainable code.
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> 
>>> On Thu, 17 Mar 2022 at 18:21, James Foster <smallt...@jgfoster.net 
>>> <mailto:smallt...@jgfoster.net>> wrote:
>>> Richard,
>>> 
>>> My _concern_ with inlining is that since it is designed to short-circuit 
>>> dynamic method lookup, it is impossible to call a _different_ 
>>> implementation. That is, you lose the opportunity to have the _receiver_ 
>>> decide how to respond to the message. You may think of it as a message, but 
>>> the caller is deciding how the receiver will respond—which largely defeats 
>>> the purpose and role of it being a message. Yes, at the machine code level 
>>> you are performing a branch instruction, but when comparing OOP to 
>>> Procedural Programming we typically make a distinction between “messages” 
>>> and "procedure calls." The distinction is that the receiver gets to decide 
>>> how to respond to a message. In C++ this is the distinction between a 
>>> “virtual" and "non-virtual" function. By inlining, you are converting the 
>>> function from a virtual function to a non-virtual function, and this can 
>>> make a difference (which is why virtual functions exist).
>>> 
>>> How should a proxy (https://en.wikipedia.org/wiki/Proxy_pattern 
>>> <https://en.wikipedia.org/wiki/Proxy_pattern>) to nil respond to the 
>>> #’isNil’ message? How should the Null Object Pattern 
>>> (https://en.wikipedia.org/wiki/Null_object_pattern 
>>> <https://en.wikipedia.org/wiki/Null_object_pattern>) respond to #’isNil’? 
>>> 
>>> And, yes, I’m sure you can come up with benchmarks that show a measurable 
>>> difference, but what is the impact in realistic code? When someone asked 
>>> about inlining #’yourself’ in GemStone I believe I measured the performance 
>>> as taking 2 nanoseconds per call (on a 2012 machine). A 10% speedup would 
>>> make it 1.8 nanoseconds. Is that worth it? Maybe, maybe not.
>>> 
>>> Note that I described my position as a “concern,” not an ideological 
>>> objection. Mostly I’m giving a rationale for something that doesn’t seem to 
>>> be explained very well for you. I accept that there may be a time for 
>>> inlining, but I can “comprehend" another side to the issue.
>>> 
>>> James
>>> 
>>>> On Mar 16, 2022, at 9:42 PM, Richard O'Keefe <rao...@gmail.com 
>>>> <mailto:rao...@gmail.com>> wrote:
>>>> 
>>>> We're still not on the same page.
>>>> You seem to have some ideological objection to inlining that
>>>> I am completely failing to comprehend.
>>>> Just because a procedure call (message send) is inlined doesn't
>>>> in the least mean it *isn't* a procedure call (message send),
>>>> just as compiling a procedure call (message send) as a jump
>>>> (last-call optimisation) doesn't mean it *isn't* a procedure
>>>> call (message send).
>>>> By the way, forget about "40 years ago".
>>>> I just did an experiment in Pharo 9, and found that
>>>> using "_ ifNotNil: " instead of "_ izNil ifFalse: "
>>>> -- where izNil is a non-inlined self == nil --
>>>> gave a 10% speedup, in a test code where real work was going
>>>> on as well.
>>>> As for turning off all inlining, what do you think that would
>>>> do to #ifFalse:ifTrue: and its relatives?  
>>>> 
>>>> 
>>>> On Thu, 17 Mar 2022 at 08:34, <s...@clipperadams.com 
>>>> <mailto:s...@clipperadams.com>> wrote:
>>>> 
>>>> 
>>>> To start with, why do you CARE whether a particular method is inlined or 
>>>> not?
>>>> 
>>>> I care because it makes “everything is a message” a lie! And I suspect (no 
>>>> proof and could be wrong) it’s an optimization that only made sense with 
>>>> the hardware constraints of 40+ years ago. Arguing against premature 
>>>> optimization is hardly something I just made up ;-)
>>>> 
>>>> This makes absolutely no sense to me. What makes you think that the 
>>>> combination "_ isNil ifFalse: [_]" will NOT be inlined?
>>>> 
>>>> I may have been unclear. My intent was to communicate: “I’d like to stop 
>>>> ALL* inlining of messages by default if possible”
>>>> 
>>>> *or as many as practical
>>>> 
>>>> The thing that rings loud alarm bells for me is there being "long chains" 
>>>> in the first place.
>>>> 
>>>> I agree that it is in general a smell, but long chains was tangential to 
>>>> the intention above
>>>> 
>>>> Can you give an example?
>>>> 
>>>> I don’t know if I can think of one that’s not contrived… Wrapping 
>>>> something external? Squeak’s AppleScript support used to mirror the 
>>>> underlying AS, which is pretty much exactly that.
>>>> 
>>>> In my own programming, I've generally found that nils turning up in the 
>>>> middle of a chain indicates a serious design error somewhere.
>>>> 
>>>> Agreed. See smell comment above.
>>>> 
>>> 
>> 
> 

Reply via email to