> On 17 Mar 2022, at 17:17, Richard Sargent > <richard.sarg...@gemtalksystems.com> wrote: > > A pragma might be the way to go. > > <noInline: #and:> for example.
There is already the #compilerOptions: Pragma, e.g.: <compilerOptions: #(- optionInlineAndOr )> For example this method compiled the and: as a real send: tt <compilerOptions: #(- optionInlineAndOr)> true and: [ false ] > > On Thu, Mar 17, 2022 at 8:17 AM Esteban Maringolo <emaring...@gmail.com > <mailto:emaring...@gmail.com>> wrote: > Just to add another argument in favor of not inlining some message > sends, is for instance in things like Glorp you need to use a special > selector for #and:/#or: message sends, breaking not only the naming > conventions but also the polymorphism. > > E.g. you have to use #AND: and #OR: to avoid inlining. > db read: YourClass where: [:inst | inst name = 'John' AND: [inst color > = 'blue' OR: [inst color = 'red']]]. > > This is not only annoying, it also causes that if you want to to use > something else as a backend (instead of Glorp), your block doesn't > work anymore, which would perfectly work for a regular collection of > elements using their underscore #and:/#or: equivalents. > > The important thing, IMO, is that the core of OO is message sending, > and enabling the receiver what to do with a message, which in the > worst case would mean "I don't understand this message". A procedure > call inverts the responsibility. A great trade off would be to be able > to enable/disable for certain things instead of it being a global > setting. > > Regards, > > Esteban A. Maringolo > > On Thu, Mar 17, 2022 at 11:53 AM 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. > >> > >> > >