ifNil / ifNotNil

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
13 messages Options
Reply | Threaded
Open this post in threaded view
|

ifNil / ifNotNil

Ian Bartholomew
Andy/Blair,

I've found myself wanting the above selectors more and more lately so I've
just put them back into my image (after an absence of a couple of years). I
know that you were having a think about how (and if) to implement in the
main image so, to prevent future confusion for me if nothing else, I was
wondering if you had reached any sort of conclusion.

FWIW, my #ifNotNil takes the receiver as an argument (seems a bit pointless
not to)

Ian


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

Dave Harris-3
[hidden email] (Ian Bartholomew) wrote (abridged):
> FWIW, my #ifNotNil takes the receiver as an argument (seems a bit
> pointless not to)

So #ifNotNil: takes an argument and #ifNil: doesn't?

The asymmetry makes me feel a bit uncomfortable. Then again, #ifNotNil:
really needs that argument sometimes, and with #ifNil: the argument will
always be nil (duh!) so there is no point.

The QKS version of Smalltalk does not require the number of arguments
supplied to match the number expected by a block. Extra arguments are
discarded, missing ones are supplied as nil by the system. This works
nicely with #ifNil:/#ifNotNil: - methods can take an argument or not, as
it suits them. This is the only solution I've seen which really strikes
me, but adopting it in Dolphin would be a more radical change than adding
a couple of methods.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      [hidden email]      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

Ian Bartholomew
Dave,

> So #ifNotNil: takes an argument and #ifNil: doesn't?
>
> The asymmetry makes me feel a bit uncomfortable. Then again, #ifNotNil:
> really needs that argument sometimes, and with #ifNil: the argument will
> always be nil (duh!) so there is no point.

I think the asymmetry is the only reason why the construct hasn't become an
standard part of the Smalltalk syntax. It does feel like it should match the
ifTrue/ifFalse format and that you should be able to exchange the blocks at
will.

Another way of implementing them, that might help with the uncomfortable
feeling, is to remove the implied syntactic link and just treat it as four
separate methods. Something like (and I don't really like these but can't
think of anything better at the moment) -

ifNil: []
ifNil: [] ifNot: [:arg | ]
ifDefined: [:arg | ]
ifDefined: [:arg | ] ifNot: []

but I'm not sure you actually gain much by doing this.

I have to say though that the first time I used it in anger, after putting
it back into my image, I forgot the block argument! <sigh>

Ian


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

Chris Uppal-3
In reply to this post by Ian Bartholomew
Ian,

> FWIW, my #ifNotNil takes the receiver as an argument (seems a bit
pointless
> not to)

I've never used the #ifNil* family since all my significant Smalltalk
has been in Dolphin, so this is just a random rant from an uniformed
observer...

Still, misc observations, in no special order:

Block arguments that I almost never use drive me nuts.  I'd rather have
the convenience of not having the block argument, and use a different
expression in the handfull of cases where I would have wanted it.  (I'm
reminded of the convention that #at:put:, etc, answer the added
object -- this is useful (but not necessary) on about 2% of occasions,
and is just a nuisance on the other %98).

I don't think the selector is especially nice.  If #ifNil: answers the
result of the block, so that

    myVar := otherVar ifNil: [23].

is usefull, then I'd suggest that a better name is #orIfNil:  Maybe you
don't agree, and it's certainly not "standard", but I think it reads
better as:

    myVar := otherVar orIfNil: [23].

Similarly, to my mind, #ifNil:else: reads better than #ifNil:ifNot:.

> Ian

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

Dmitry Zamotkin-2
In reply to this post by Ian Bartholomew
Construction like "ifNil / ifNotNil" may be useful in lazy initialization.

Classical example:

MyObject>>foo
    foo isNil ifTrue: [ foo := self newFoo ].
    ^ foo

If I could write:

MyObject>>foo
    ^ foo ifNilBecome: self newFoo

it would be more clear in syntax ( and typing :-)
but, I dont see a straight way for realization...

Dmitry Zamotkin


"Ian Bartholomew" <[hidden email]> wrote in message
news:JmPH6.22893$[hidden email]...
> Andy/Blair,
>
> I've found myself wanting the above selectors more and more lately so I've
> just put them back into my image (after an absence of a couple of years).
I
> know that you were having a think about how (and if) to implement in the
> main image so, to prevent future confusion for me if nothing else, I was
> wondering if you had reached any sort of conclusion.
>
> FWIW, my #ifNotNil takes the receiver as an argument (seems a bit
pointless
> not to)
>
> Ian
>
>
>
>
>
>


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

Ian Bartholomew
In reply to this post by Chris Uppal-3
Chris,

> Block arguments that I almost never use drive me nuts.  I'd rather have
> the convenience of not having the block argument, and use a different
> expression in the handfull of cases where I would have wanted it.

Yes, that would be an option. It fits in with what I said yesterday about
removing the coupling between ifNil and ifNotNil and just treating them as
4, or 6, separate-but-related method definitions.

> I don't think the selector is especially nice.

No, neither do I. It's default to find something general enough to replace
it though

> Similarly, to my mind, #ifNil:else: reads better than #ifNil:ifNot:.

I was trying to match things like #detect:ifNone: and #at:ifAbsent:. Perhaps
#ifNil:butIfNot: ... or perhaps not <g>

I suppose the other question is whether it is worth it at all?. I've been
trying to think of occasions where it would make a difference to existing
code, make it more readable or more efficient, and came up with the
following generalisations - I'm sure there are more but I can't think of
them offhand (anyone?)

x * (y ifNil: [1])
or
x * (y ifNil: [y := 1])
or
x * (y := y ifNil: [1])
(avoids a separate test to set var)

x := FileOpenDialog showModal
    ifNil: [^self]
    ifNotNil: [:arg | FileStream read: arg]
(avoids an extra var)

^x := x ifNil: [123]
(lazy initialisation)

assuming Object>>isNil and UndefinedObject>>ifNotNil both answer the
receiver and the others answer the block value.

What's the general feeling. Does ifNil/ifNotNil add anything or is it just a
waste of image space?

Ian


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

Chris Uppal-2
In reply to this post by Chris Uppal-3
I wrote:

> so this is just a random rant from an uniformed observer...

Urk!  That should be "uninformed", I haven't worn a uniform since my school
days...

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

Chris Uppal-2
In reply to this post by Ian Bartholomew
Ian,

> > Block arguments that I almost never use drive me nuts.  I'd rather have
> > the convenience of not having the block argument, and use a different
> > expression in the handfull of cases where I would have wanted it.
>
> Yes, that would be an option. It fits in with what I said yesterday about
> removing the coupling between ifNil and ifNotNil and just treating them as
> 4, or 6, separate-but-related method definitions.

I don't think I expressed myself very clearly?  I meant that even though the
argument to #ifNotNil:'s block would be useful sometimes, I didn't think
it'd be useful enough to "earn" the brainspace it'd take up.  I'd rather not
have it, and code around the cases where it would have been useful, than
have it and have to remember about it even when I didn't need it.

Actually, having thought about it a bit more, I'm not too sure about that
anymore.  In any case I think that the biggest gain would be improved
compatibility with other Smalltalks, rather than greater ease of expression
for myself, so I think the important thing is to do what the other
Smalltalks do.  I remember that Blair said there was an issue with which
version of #IfNil: (etc) to be compatible *with*, but I can't remember the
details.

VW doesn't seem to have the #ifNil family, so I suppose it's a choice
between Squeak or VAST compatibility (that's just a guess -- I've never even
seen VAST).  If so then maybe a good way to get a handle on which route is
best is to ask on c.l.s what the VW users have found it expedient to do.

> I suppose the other question is whether it is worth it at all?. I've been
> trying to think of occasions where it would make a difference to existing
> code, make it more readable or more efficient, [..]

I don't think it'd *ever* be more efficient -- at least not unless the
compiler recognised it as a special form to be inlined (which I think I'd
oppose).  OTOH a *large* number of expressions in the image could be made
slightly more elegant.

But, for myself, I don't think I'd use them much.  The existing form is
perfectly transparent, so I can't really imagine myself choosing to use the
shorter form when I know that it's (microscopically) slower.  I'm not really
such an efficiency nut as that makes it sound; the way I see it is that I'd
have a choice:

    (1) Make a rule, always to use the elegant form and never think about
         efficiency.
    (2) Make a rule, always to use the efficient form.
    (3) Stop and think each time I use #isNil to decide whether to go for
         elegance or speed.

Choice (3) would be kind of stupid, so it comes down to (1) or (2).  I find
(1) a bit dubious, so I think I'd end up making (2) my rule.  (Also, if I
did go with (1) then I'd be *massively* bugged by all my existing code which
still used the old form -- people tell me I'm odd...)

So, for me, the actual gain would only be increased easy of porting code
from Squeak (or similar).

> What's the general feeling. Does ifNil/ifNotNil add anything or is it just
a
> waste of image space?

On the whole, I think they'd be worth the space.

> Ian

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

Dave Harris-3
[hidden email] (Chris Uppal) wrote
(abridged):
> I don't think I expressed myself very clearly?  I meant that even
> though the argument to #ifNotNil:'s block would be useful sometimes,
> I didn't think it'd be useful enough to "earn" the brainspace it'd
> take up.  I'd rather not have it, and code around the cases where
> it would have been useful, than have it and have to remember about
> it even when I didn't need it.

If it doesn't take an argument, I don't see much advantage from using it.
Compare:

     |value|
     value := anObject someMessage.
     value notNil ifTrue: [value someOtherMessage].
 
versus:

     |value|
     value := anObject someMessage.
     value ifNotNiL: [value someOtherMessage].

versus:    
     |value|
     (value := anObject someMessage) ifNotNiL: [
           value someOtherMessage].
     
versus:
     anObject someMessage ifNotNiL: [:value|
           value someOtherMessage].


I don't think the second had much advantage over the first. The third is
a bit shorter but only at the cost of introducing brackets and general
complication.

The last does seem genuinely clearer to me, because it avoids having to
name and declare the temporary variable at the top of the method. The
variable's scope is smaller and it has its correct, non-nil value for all
of it. To put it another way, there is no mutable state, no destructive
assignment.


>     (1) Make a rule, always to use the elegant form and never think
>         about efficiency.
>     (2) Make a rule, always to use the efficient form.
>     (3) Stop and think each time I use #isNil to decide whether to go
>         for elegance or speed.
>
> Choice (3) would be kind of stupid, so it comes down to (1) or (2).  I
> find (1) a bit dubious, so I think I'd end up making (2) my rule.

I'd go for:
     (4) Use the elegant form unless there is a pressing concern
         about speed.

This is similar to (3), except that "Stop and think each time" gives the
wrong impression.


I've found the #ifNotNil: pattern above to be rarer than the lazy
initialisation one. There are lots of different ways to do lazy
initialisation, and I would much prefer to settle on one canonical form.
Having a specific message may encourage that.

  Dave Harris, Nottingham, UK | "Weave a circle round him thrice,
      [hidden email]      |   And close your eyes with holy dread,
                              |  For he on honey dew hath fed
 http://www.bhresearch.co.uk/ |   And drunk the milk of Paradise."


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil

David Simmons
Just to chime in my two bits...

-- Dave S. [SmallScript, QKS Smalltalk/SmalltalkAgents]

==============================

Recap of the arg/no-arg design issue:
------------------------------------
If we were designing this for smalltalk dialects that require block
#value... messages to have parity with the number of arguments declared in a
block, we must decide on the number of arguments (0 or 1) for the various
message forms.

singular forms:
---------------
    ifNil: nilValuable

    ifNotNil: notNilValuable

combinatoric forms:
------------------
    ifNil: ... ifNotNil: ...

    ifNotNil: ... ifNil: ...


Where the protocol for <Block> instances would be:

    notNilValuable --> [:nonNilValue| "arg required"]

    nilValuable    --> ["no arg provided"]

-----
These forms have proven to be a very useful construct in QKS Smalltalk since
I added it to the core frameworks ~1992.

There was a lot of comp.lang.smalltalk debate on these forms because until
1995, (for these messages and #isNil,#notNil) our Smalltalk implementation
of these methods treated <0> and <nil> the same way. In 1995 I swept the QKS
Smalltalk image and updated it, at which point we removed the <0> support;
at which time a new set of messages #ifZero:, #ifNotZero:,... was added.

Around 1994, one of our users wanted an even shorter (binary message) form
for some common usage operations. They had defined #? to be the same as
#ifNil:.

I really liked that #? variant and added it, as well as defining a #!?
variation, to our base image ~1994. They have proven to be really attractive
and I find myself using them often.

So, this leads to:

    ? nilValuable

    !? notNilValuable

I.e., the messages #? and #ifNil: do the exact same thing; and similarly for
#!? and #ifNotNil:. I'm assuming the reader recognizes that contract for the
#(? !? ifNil: ifNotNil: ...) forms is based on #value... messages
(<IValuable> protocol).
-----

The SmallScript and QKS Smalltalk compilers have always inlined all the
original forms, and when I added core image support for #?, #!?, #ifZero:,
... they too became inlined forms. An astute reader will readily observe
that inlining is never an issue because the <nil> messages apply to every
object/class with a uniform meaning -- special inlining is done for the zero
forms (compiler actually sends the message #isZero for its testing).

Some patterns of use:
--------------------
method [
foobar
    ^foobar ? [foobar := ...init-expr...]
].

method [
messageName

    "" This pattern is especially useful in various
    "" looping/control-flow situations
    expr use: (expr ? [^nil]) doSomethingExpr
].

----

It is also convenient if your ST-IDE/editor supports keybindings for editing
operations like:

 - parenQuoteSelectorOrRestOfLine
   (...)

 - blockQuoteSelectorOrRestOfLine
   [...]

In our editors, hitting ctl-( will parenQuote the current selection, or if
none, the text from the caret position to the last non-whitespace in the
line. Similar for various other quote forms ctl-[, ctl-', ctl-", etc.


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil (selector name clashes)

David Simmons
"Paolo Bonzini" <[hidden email]> wrote in message
news:[hidden email]...

> > Around 1994, one of our users wanted an even shorter (binary
> > message) form for some common usage operations. They had
> > defined #? to be the same as #ifNil:.
>
> This is not good in my opinion.  One is free to define binary selector
> for whatever they like, but when you add them to Object you *must*
> expect clashes!
>
> For example, GNU Smalltalk's i18n library uses #? as a shortcut
> for #print: so that you can do things like
>
>    "nls is an instance of LcMessagesDomain"
>    nls ? 'a string to be translated'
>
>    myLcMonetary ? 123456
>    LcTime posix ? DateTime now
>    LcNumeric default ? 123456
>
> but they are implemented in the Locale class!
>
> For this reason I am against adding arbitrary binary
> selectors to Object, and much more against inlining!

Interesting.

First, let me be clear that there is nothing "arbitrary" about the
SmallScript or QKS use of the #? and #!? selectors; noting they've been in
our images (as described) for some 7 years. Having done a simple scan of use
in our various tools and libraries, there are literally thousands of methods
that use these forms. So it is certainly a very useful and quickly
recognized pattern.

However, that rationale aside, SmallScript is explicitly designed to support
multi-methods and scoped messaging operations to reduce selector name
clashing. Thus, given its design philosphy, SmallScript should be able to
support both the <nil> form and locale-usage definitions, you've described,
without a problem.

"" Here is a sample of such method declarations

method behavior: ILocale [
? <String> aString
    ...
].

method behavior: ILocale [
? <Time> aTime
    ...
].

method behavior: ILocale [
? <Number> aNumber
    ...
].

---- SIDEBAR on I18N/Locale stuff ----
This commentary is specific to globalization I18N related work, and, in
general terms, has nothing to do with the basic issue of selector name
clashes brought up by the preceding poster (Paolo).
---
I've done very extensive work in the area of
globalization/internationalization/localization (incl. japanization) on all
the major OS platform groups over the last 14 years, have quite a few
friends in this field, and have served on the LI18NUX standards group as
well.

So, from that vantage point, I think that using #?, while certainly
convenient as a terse binary message, it is not particularly intuitive and
in-and-of-itself insufficient for serious locale work.

In looking for a terse "binary-message" mapping selector for this GNU usage
pattern I might have chosen something from one of these lists:

    <=
    <<=

Which suggests a flow of the data through a (stream-like) locale
transformation/presentation filter.

    stdout cr << (locale <= 'literal').

However, in a convenience scenario, I would have expected all the
user/local/policy configuration and settings to be part of the thread local
state/variables/configuration.

Thus a more convenient forms might be:

    #(localize localeForm asLocalForm ...)

    "" Thus our short form, a really convenient unary selector,
    "" might be.
    stdout cr << 'literal' localize.

---- END SIDEBAR ----

Based on the actual use pattern of #? and #!? I just spent five minutes and
modified the inline mechanism in SmallScript to only perform inlining for
the multi-method variant of "<any> ? <Block>". (** its taken me far longer
to write this post ** :)

With that change inline optimization still covers 99.9% of the actual use
cases of #? while leaving the selector free and clear for almost any other
purpose. ** and at some level, this pattern could be recognized and
optimized soley at the JIT layer -- it's just simpler for the compiler to
inline the block forms **

An arbitrary package or module is free to define any other variants. I.e.,
the #? and #!? for the 99% usage case will be inlined and optimized *and*
the GNU locale code would compile and work in SmallScript (Smalltalk)
without conflict/problems.

I should also comment that single-character binary selectors (and even
common two symbol ones) should be used judiciously when designing a wholly
"new semantic" for them within a framework.

All prior issues aside, their (binary selectors) very terse convenience also
means that they may well not be "self revealing" as to their intent. As a
result, if their intent is not "common/universal", or "obvious" within their
context of use; then they are probably a poor choice. In that circumstance,
if binary selectors are really desireable/needed, a more esoteric
combination should be used to avoid confusion among diverse semantic
applications of said selectors.

---
I should also point out that in QKS Smalltalk, we have consistently use the
binary selector character $!, to represent the "not" case of any related
binary message pattern. We have also tried to choose binary message patterns
that corresponded to "popular" use of similar messages/operators in the
mainstream of "programming languages".

Thus:

  =  vs !=  "" The ~= pattern is deprecated (not recognized in the
mainstream)
  == vs !== "" The ~~ pattern is deprecated (not recognized in the
mainstream)
  ?  vs !?

And, for prefix selector forms {+,-,~,!}

  !expr   "" Compiler generates as: "expr not"

Our frameworks support various deprecated (or multi-dialect conflicting)
message forms. Our AOS Platform virtual machine technology and object model
provide the ability to alias selectors. This aliasing is used to efficiently
and consistently treat deprecated forms as if they were the preferred form
in all behavioral circumstances.

-- Dave S. [SmallScript; QKS Smalltalk/SmalltalkAgents]

>
> Paolo
>
>
> --
> Posted from relay3.inwind.it [212.141.53.74]
> via Mailgate.ORG Server - http://www.Mailgate.ORG


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil (selector name clashes)

Kevin Szabo
In article <9dHJ6.43101$[hidden email]>,
David Simmons <[hidden email]> wrote:
| Based on the actual use pattern of #? and #!? I just spent five minutes and
| modified the inline mechanism in SmallScript to only perform inlining for
| the multi-method variant of "<any> ? <Block>". (** its taken me far longer
| to write this post ** :)

Dave, when you mention inlining here, do you mean that the compiler
avoids building block instances and sending #value to them, and instead
it generates VM codes for the expressions (inline) with jumps/gotos
around the if/else portions?

Regards,
Kevin
--
Kevin Szabo ---- "I am Fudd of Borg. Wesistance is usewess!"


Reply | Threaded
Open this post in threaded view
|

Re: ifNil / ifNotNil (selector name clashes)

David Simmons
"Kevin Szabo" <[hidden email]> wrote in message
news:9daqal$48k$[hidden email]...
> In article <9dHJ6.43101$[hidden email]>,
> David Simmons <[hidden email]> wrote:
> | Based on the actual use pattern of #? and #!? I just spent five minutes
and
> | modified the inline mechanism in SmallScript to only perform inlining
for
> | the multi-method variant of "<any> ? <Block>". (** its taken me far
longer
> | to write this post ** :)
>
> Dave, when you mention inlining here, do you mean that the compiler
> avoids building block instances and sending #value to them, and instead
> it generates VM codes for the expressions (inline) with jumps/gotos
> around the if/else portions?

Simple answer:
-------------
   Yup.

More Detail: (about what happens if a block is not inlined)
-----------
The language level compiler only builds block instances for "static/literal"
(clean) blocks; which it stores in a method's literals. For which it emits a
load-static-block opcode which includes the branch lable for skipping to the
end of the opcodes for the block.

----
Blocks that require per/block-instance reference to some form of contextual
(instantiation-context) state must be created at execution time.

For blocks that must be instantiated (created) at execution time, the
language level compiler emits a load-new-block opcode which includes the
branch label for skipping to the end of the opcodes for the block.

As the JIT's analyzer examines methods and their opcodes, it determines all
the remaining characteristics of a "non-static/non-literal" (dirty) block on
the fly. Hence, the reason why I suggested in some post or other that the
JIT could actual handle the inlining directly by avoiding the actual
creation and loading of a new-block and subsequent #value... message.  It
was just easier to have the language compiler do it.

-- Dave S. [www.smallscript.net; QKS Smalltalk/SmalltalkAgents]

>
> Regards,
> Kevin
> --
> Kevin Szabo ---- "I am Fudd of Borg. Wesistance is usewess!"