On Mar 17, 2022, at 2:27 AM, Richard O'Keefe <raoknz@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 allother than "is the receiver identical to nil" you *WILL*(not may) confuse me. This extends to things that happennot to be inlined: if even a god-like Smalltalker likeAndres Valloud overloads #, to something other than "combinethe collection that is the receiver with the collection thatis 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 inconsistentoverload anywhere, I can no longer understand sends of thatselector anywhere. One of the things to like about Traitsis that you can say "this class doesn't just *happen* tohave selectors x and y, it has them *because* it has thiswhole consistent bundle of selectors."There are more annotations documented for my Smalltalkcompiler than are actually implemented. One that *is*implemented is <doNotOverride>, and it has caught moremistakes than I care to admit to. It's particularlyimportant for a bundle of methods with varying argumentsthat are meant to be routed through a single method,which *is* meant to be overridden. It makes sure thatI override the *right* method. (Take #= and #~= as anobvious example.)Once you start down the path of lying about things like #isNilyou find that EITHER you have to go very far down that pathand override #== and #instVarAt: and a whole lot of otherthings OR you are working with a semantically incoherent system."How should a proxy (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 nilcannot. Use another selector, #isEffectivelyNil, or whatever reveals yourintentions, and give it what semantics you find useful."How should the 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 somethingthat *isn't* nil, that has quite a different protocol. If you callsomething that is supposed to return either a Foo or a NullFoo, andit gives you nil instead, there is a BUG in what you just called sothe sooner you find out the better. What you want isFoo >> isNullFoo ^falseNullFoo >> isNullFoo ^trueand 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 ObjectI've used an all-purpose #isMissing instead of a task-appropriate#isNullFoo, but then I figured out what I was doing wrong. Youlook at the code, you say "there's *something* off here, but I don'tknow what." But when you ask "what, exactly, does this method MEAN?"you realise "oh, THAT'S what I was doing wrong." #isMissing told meit was a NullPerson or a NullAddress or a NullPartsList or ... butin 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 neverenough, 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 phoneThere's room for three *different* null objects there.Should we have UnknownNumberOfActualPhone to answer false to #isniland 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 by10%, it was the *whole* matrix-munching benchmark thatspeeded up. Certainly not a big deal, but it's notsomething I'd be happy to give up in order to get lessmaintainable code.On Thu, 17 Mar 2022 at 18:21, James Foster <smalltalk@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) to nil respond to the #���isNil��� message? How should the 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.JamesOn Mar 16, 2022, at 9:42 PM, Richard O'Keefe <raoknz@gmail.com> wrote:We're still not on the same page.You seem to have some ideological objection to inlining thatI am completely failing to comprehend.Just because a procedure call (message send) is inlined doesn'tin 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 procedurecall (message send).By the way, forget about "40 years ago".I just did an experiment in Pharo 9, and found thatusing "_ 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 goingon as well.As for turning off all inlining, what do you think that woulddo to #ifFalse:ifTrue: and its relatives?On Thu, 17 Mar 2022 at 08:34, <sean@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.