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> 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