version of -> short-circuiting on nil

258 views
Skip to first unread message

Laurent PETIT

unread,
Mar 9, 2009, 8:12:26 PM3/9/09
to clo...@googlegroups.com
Hello,

When interacting with java code, and maybe in other pure clojure situations as well (but I have not encountered the case myself), I was faced with writing boiler plate code to check whether the return values of a chain of calls to successive instance members were null before continuing ...

something like writing :
(let [person (get-the-person)]
  (when-not (nil? person)
    (let [address (.getAddress person)]
      (when-not (nil? address)
         (let [street (.getStreet address)]
            (when-not (nil? street)
               (do-something-finally-with-street street)))))

I know it's somewhat encouraging the violation of the law of Demeter, but still, there are a lot of libraries that force you to write this, so I wrote a version of -> that short-circuits the computation if one of the intermediate threaded value is nil.

I named it ?-> because some other OO languages already use the ? as a "maybe" reminder :
person?.address?.street?.call-finally-something-on-street

With this macro, the above code can be written as :

(?-> (get-the-person ...) .address .street do-something-finally-with-street)

Here is the macro. If you think it could be an interesting addition to clojure-contrib, feel free to add it (maybe re-explain me how to trigger the contribution stuff) :

(defmacro ?->
  "Same as clojure.core/-> but returns nil as soon as the threaded value
   is nil itself (thus short-circuiting any pending computation).
   Examples :
   (?-> \"foo\" .toUpperCase (.substring 1)) returns \"OO\"
   (?-> nil .toUpperCase (.substring 1)) returns nil
   "
  ([x form]
    (let [list-form (if (seq? form) form (list form))]
      `(let [i# ~x]
         (when-not (nil? i#)
           (~(first list-form) i# ~@(rest list-form))))))
  ([x form & more]
    `(?-> (?-> ~x ~form) ~@more)))

Jason Wolfe

unread,
Mar 9, 2009, 9:11:27 PM3/9/09
to Clojure
> (let [person (get-the-person)]
>   (when-not (nil? person)
>     (let [address (.getAddress person)]
>       (when-not (nil? address)
>          (let [street (.getStreet address)]
>             (when-not (nil? street)
>                (do-something-finally-with-street street)))))
>


?-> sounds potentially useful to me, but let me also point out that
you could simplify the above to:

(when-let [person (get-the-person)]
(when-let [address (.getAddress person)]
(when-let [street (.getStreet address)]
(do-something-finally-with-street street))))

Not quite

(?-> (get-the-person) #(.getAddress %) #(.getStreet %) do-something)

but it's halfway there at least.

Info on contributing is here:

http://clojure.org/contributing

The first step is to get your CA signed and in the mail.

Thanks,
Jason

Laurent PETIT

unread,
Mar 10, 2009, 4:08:42 AM3/10/09
to clo...@googlegroups.com
Hello,

2009/3/10 Jason Wolfe <jaw...@berkeley.edu>


> (let [person (get-the-person)]
>   (when-not (nil? person)
>     (let [address (.getAddress person)]
>       (when-not (nil? address)
>          (let [street (.getStreet address)]
>             (when-not (nil? street)
>                (do-something-finally-with-street street)))))
>


?-> sounds potentially useful to me, but let me also point out that
you could simplify the above to:

(when-let [person (get-the-person)]
 (when-let [address (.getAddress person)]
   (when-let [street (.getStreet address)]
     (do-something-finally-with-street street))))

No, because here, you could also short-circuit on the value false , and not only nil.
And it's definitely what I had in a first attempt, and wanted to avoid : having to think about variable names and add several nested levels of lets.



Not quite

(?-> (get-the-person) #(.getAddress %) #(.getStreet %) do-something)

but it's halfway there at least.

Info on contributing is here:

http://clojure.org/contributing

The first step is to get your CA signed and in the mail.

Yes you're right, I already have sent my CA, and will re-read this page.

But I thought that before proposing something (even for clojure-contrib), it would be interesting to see if what I would like to propose sounds interesting to more than one person :-) and if somebody having commit rights on clojure-contrib informally accepts the contrib, before creating an issue with the patch attached ?
 

Thanks,
Jason






RZez...@gmail.com

unread,
Mar 12, 2009, 1:32:59 AM3/12/09
to Clojure
Not that I have any immediate use for this at the moment, but I +1
your proposal. I make use of the ?. operating in Groovy, and it can
be helpful.

On Mar 10, 4:08 am, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> Hello,
>
> 2009/3/10 Jason Wolfe <jawo...@berkeley.edu>

Stuart Sierra

unread,
Mar 12, 2009, 1:57:55 PM3/12/09
to Clojure
On Mar 9, 8:12 pm, Laurent PETIT <laurent.pe...@gmail.com> wrote:
> When interacting with java code, and maybe in other pure clojure situations
> as well (but I have not encountered the case myself), I was faced with
> writing boiler plate code to check whether the return values of a chain of
> calls to successive instance members were null before continuing ...

This has been discussed here:
http://groups.google.com/group/clojure/browse_thread/thread/c4eb149248859a18/aab137e1d9363f67

-Stuart Sierra

Laurent PETIT

unread,
Mar 12, 2009, 5:45:31 PM3/12/09
to clo...@googlegroups.com
Oh, I should have search through the ml first ...

I see that we came to almost exactly the same code ;-)

So it seems that finally, there are more than persons named "Stuart" that would like to see this function somewhere in clojure or clojure-contrib.

I have the following questions :

 * Concerning the function : the previous post was talking about .. , when I was talking about -> . To be honest, I don't know if there is a difference. I think there is one, in that -> is the generalized version of .., working not only on java instances, but also with any clojure function result ...

Should both .. and -> be provided with an equivalent "nil safe" form, or just -> ?

 * Concerning the correctness of the function presented in the thread you mentioned : note that the version I provided and named (for the moment) ?-> worked also if the very first argument is nil, that is (?-> nil .toUpperCase (.substring 1)) works by returning nil (it seems to me that the versions in the mentioned thread would have thrown a NullPointerException in this case ?)

* Concerning the name to give to this function : maybe ->? instead of ?->, if you think we can live with this "violation" to the predicate convention ?

* Concerning the namespace where it should reside : clojure.core ? clojure.contrib.core  ? clojure.contrib.macros ? something else ?

Thanks in advance,

--
Laurent


2009/3/12 Stuart Sierra <the.stua...@gmail.com>

Stephen C. Gilardi

unread,
Mar 12, 2009, 6:04:12 PM3/12/09
to clo...@googlegroups.com

On Mar 12, 2009, at 5:45 PM, Laurent PETIT wrote:

> * Concerning the name to give to this function : maybe ->? instead
> of ?->, if you think we can live with this "violation" to the
> predicate convention ?

I like these:

.?.
-?>

They fit with the criteria that Rich laid down in the other thread
(paraphrasing):

neither a leading nor a trailing question mark use

These spellings fit that rule and I also like them in their own right.

--Steve

Mark Volkmann

unread,
Mar 12, 2009, 9:45:56 PM3/12/09
to clo...@googlegroups.com

Given a choice between the two, I'd choose -?>

--
R. Mark Volkmann
Object Computing, Inc.

Stephen C. Gilardi

unread,
Mar 12, 2009, 11:24:17 PM3/12/09
to clo...@googlegroups.com

On Mar 12, 2009, at 9:45 PM, Mark Volkmann wrote:
>
> On Thu, Mar 12, 2009 at 5:04 PM, Stephen C. Gilardi
> <sque...@mac.com> wrote:
>>
>> I like these:
>>
>> .?.
>> -?>

> Given a choice between the two, I'd choose -?>

The proposal was for naming "nil-safe" versions of the existing .. and
-> functions.

(-> nil (. toString)) ==> NullPointerException

(-?> nil (. toString)) ==> nil


(.. nil (toString)) ==> NullPointerException

(.?. nil (toString)) ==> nil


Note: these are just simple examples. The point isn't the one
argument, literal nil case, but the case where many operations are
"chained" or "threaded" and at each point the propagated result may be
nil.

--Steve

Laurent PETIT

unread,
Mar 13, 2009, 5:45:06 AM3/13/09
to clo...@googlegroups.com
OK, so I think the consensus is on the names .?. and -?> . No problem with me.

I'm willing to write the patch, but which patch ? I mean, do these macros deserve their own file (maybe not) ?
Maybe adding them to the existing clojure.contrib.macros ?
Or else, I was thinking about creating a new file named clojure.contrib.core since these are slight variants of existing core functions ?

2009/3/13 Stephen C. Gilardi <sque...@mac.com>

Laurent PETIT

unread,
Mar 13, 2009, 8:36:43 PM3/13/09
to clo...@googlegroups.com
Issue 34 ( http://code.google.com/p/clojure-contrib/issues/detail?id=34 ) created with patch (both -?> and .?.  defined in clojure.contrib.core,  with unit tests in clojure.contrib.core.tests

--
Laurent

3/13 Laurent PETIT <lauren...@gmail.com>

Laurent PETIT

unread,
Mar 22, 2009, 5:42:26 AM3/22/09
to clo...@googlegroups.com
OK, Stephen checked this in, thank you very much !

Two new symbols:

clojure.contrib.core/.?.
clojure.contrib.core/-?>


Regards,

--
Laurent

2009/3/14 Laurent PETIT <lauren...@gmail.com>

Michael Wood

unread,
Mar 22, 2009, 2:28:21 PM3/22/09
to clo...@googlegroups.com
On Sun, Mar 22, 2009 at 11:42 AM, Laurent PETIT <lauren...@gmail.com> wrote:
> OK, Stephen checked this in, thank you very much !
>
> Two new symbols:

As was pointed out to me recently, http://clojure.org/reader says:

"Symbols beginning or ending with '.' are reserved by Clojure."

So, is .?. not a symbol (because it's called at compile time and at
runtime there is no such thing as .?.)? i.e. what exactly is the
definition of a symbol, and do the names of macros count?

> clojure.contrib.core/.?.
> clojure.contrib.core/-?>

--
Michael Wood <esio...@gmail.com>

Stephen C. Gilardi

unread,
Mar 22, 2009, 3:45:44 PM3/22/09
to clo...@googlegroups.com

On Mar 22, 2009, at 2:28 PM, Michael Wood wrote:

> As was pointed out to me recently, http://clojure.org/reader says:
>
> "Symbols beginning or ending with '.' are reserved by Clojure."
>
> So, is .?. not a symbol (because it's called at compile time and at
> runtime there is no such thing as .?.)? i.e. what exactly is the
> definition of a symbol, and do the names of macros count?


clojure.contrib.core/.?. is a symbol. Thanks for pointing out that it
is one that's reserved to Clojure.

The reference you gave is the canonical documentation for symbols in
Clojure. Symbols in Clojure are used to name things including macros.

The name clojure.core/.. works and its name isn't a problem because
it's part of Clojure. The case of ".?." is a little unusual (regarding
using this kind of name) in that it's named as a variation of ".." for
the purpose of proposing it as a possible addition to Clojure at some
point.

If we can't get Rich's blessing for this name to be part of contrib,
we'll rename it.

Rich, should we rename clojure.contrib.core/.?. to avoid using a name
reserved to Clojure?

--Steve

Meikel Brandmeyer

unread,
Mar 22, 2009, 4:43:01 PM3/22/09
to clo...@googlegroups.com
Hi,

Am 22.03.2009 um 20:45 schrieb Stephen C. Gilardi:

> clojure.contrib.core/.?. is a symbol. Thanks for pointing out that
> it is one that's reserved to Clojure.
>

> Rich, should we rename clojure.contrib.core/.?. to avoid using a
> name reserved to Clojure?

I'd like to throw in another thought:
Is .?. necessary? -> does the same job as .. by
virtue of the .method notation, but is more general.
So, why not get rid of .. and .?. completely?

Sincerely
Meikel

Stephen C. Gilardi

unread,
Mar 22, 2009, 9:05:02 PM3/22/09
to clo...@googlegroups.com

On Mar 22, 2009, at 4:43 PM, Meikel Brandmeyer wrote:

> Is .?. necessary? -> does the same job as .. by
> virtue of the .method notation, but is more general.
> So, why not get rid of .. and .?. completely?

That sounds right to me, Meikel. I'm in favor of keeping only -?> .

--Steve

Graham Fawcett

unread,
Mar 22, 2009, 10:30:11 PM3/22/09
to clo...@googlegroups.com

Might I suggest "maybe->" ? This function has a strong similarity to
the Maybe monad in Haskell and similar languages.

"and->" would be another less Haskelly candidate, and is reminiscent
of Scheme's "and-let*" which is also sort-of similar.

Best,
Graham

Laurent PETIT

unread,
Mar 23, 2009, 6:49:39 PM3/23/09
to clo...@googlegroups.com
Hello,

there has been plenty of time to speak about that in the previous thread on the subject, and it's a shame these interesting proposals had to wait for the release of the functionality to pop up :-).

But I think it's my fault: when nobody did additional comments on the thread, I considered one of the last proposals as silently accepted by most, and since it was one of Rich's proposals to use .?. and -?>, I took them. So I did not close the "brainstorming" with a "if everybody agrees, I'll use these names ... and write the patch".

Concerning the fact that .. could be removed from clojure.core, why not. I suggest that the .?. version be removed when (if) .. is removed.

Concerning the names, shouldn't maybe-> be reserved for the real monadic version (I haven't seen how monads are implemented in clojure, and I'm not sure the signature of a maybe-> monad would be the same as -?>) ?

Regards,

--
Laurent

2009/3/23 Graham Fawcett <graham....@gmail.com>

Graham Fawcett

unread,
Mar 24, 2009, 2:20:00 PM3/24/09
to clo...@googlegroups.com
On Mon, Mar 23, 2009 at 6:49 PM, Laurent PETIT <lauren...@gmail.com> wrote:
> Hello,
>
> there has been plenty of time to speak about that in the previous thread on
> the subject, and it's a shame these interesting proposals had to wait for
> the release of the functionality to pop up :-).
>
> But I think it's my fault: when nobody did additional comments on the
> thread, I considered one of the last proposals as silently accepted by most,
> and since it was one of Rich's proposals to use .?. and -?>, I took them. So
> I did not close the "brainstorming" with a "if everybody agrees, I'll use
> these names ... and write the patch".

No worries, Laurent. Sorry for not joining the discussion earlier. :-)
Actually I think the "-?>" squiggle is quite a good name for this
function.

Best,
Graham
Reply all
Reply to author
Forward
0 new messages