Deref Nil

309 views
Skip to first unread message

Deon Moolman

unread,
Sep 9, 2016, 4:10:11 AM9/9/16
to Clojure
Hey all,

I'm having some pain with atoms and dereferencing nil - mostly around my functions sometimes returning an atom and other times nil - I don't really want to create a special 'nil' atom and do the bits for returning that and I don't want to be checking nils absolutely everywhere when nil-punning works perfectly otherwise, so I was wondering what the pitfalls of this approach would be?

(extend-type nil IDeref
  (-deref [_] nil))

Effectively, make @nil nil-pun to nil. makes sense to me?

Cheers,
 - Deon

Stuart Sierra

unread,
Sep 9, 2016, 8:40:46 AM9/9/16
to Clojure
This approach would only work in ClojureScript, where IDeref is defined as a Protocol. In Clojure(JVM), the core functions are defined in terms of Java interfaces, which are not extensible to `nil`.

I don't find atom-or-nil to be a common value pattern. But if it's something you encounter frequently, you could work with it by either:
- defining functions to return (atom nil) instead of nil
- defining your own nil-safe version of deref

–S

Deon Moolman

unread,
Sep 9, 2016, 9:04:49 AM9/9/16
to clo...@googlegroups.com
Thanks Stuart,

Ah, yes - in my case I am using ClojureScript - I assumed it worked the same in Clojure - oops!

Ok, I'll likely just step around it by making the functions returning (atom nil) - but I assumed there would be reasoning behind the core deref not nil-punning to nil? I'm keen to hear the reasoning just to understand the decisions and thoughts behind it, more than anything else though.

Cheers,
 - Deon

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+unsubscribe@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/ot_sD2sJW0A/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Sean Corfield

unread,
Sep 9, 2016, 12:56:00 PM9/9/16
to Clojure Mailing List

Like Stuart, I don’t encounter atom-or-nil as a common pattern – could you explain why you have functions that might return an atom or might return nil?

 

FYI, We have about 40,000 lines of Clojure at World Singles and just a handful of atoms (and most of those are going away as we refactor the code to use Component more extensively).

 

Sean Corfield -- (970) FOR-SEAN -- (904) 302-SEAN
An Architect's View -- http://corfield.org/

"If you're not annoying somebody, you're not really alive."
-- Margaret Atwood

Deon Moolman

unread,
Sep 10, 2016, 1:12:13 PM9/10/16
to clo...@googlegroups.com
Hi Sean,

Good point - as you have noticed, my use case is in ClojureScript - I'm using reagent cursors (hence the deref) fairly heavily to chop my main atom up and send only the relevant bits to components, but still allow them to locally modify their state. 

In my specific case, I do lookups for parts of the tree that may not exist, and in that case 'nil' is the obvious return value.

Cheers,
 - Deon

--

Timothy Baldridge

unread,
Sep 10, 2016, 3:08:28 PM9/10/16
to clo...@googlegroups.com
I've worked with a model much like this, and as an experience report: it resulted in a lot of pain. Atoms inside atoms, or really more than one atom for the entire state of your app results in to many sources of mutability. On top of that, you have the problem of async updates: some part of your state may or may not exist depending if some async call to the server has completed yet. I can't say much more without seeing your code, but I would use this as an opprotunity to re-think the design of the app a bit. 

If you are getting nils in unexpected places you can either nil-prune (what you're asking for here), or you can ask "why am I getting a nil here when I didn't expect it...how can I keep from doing this in the future?". Perhaps the answer is as simple as never calling the render function of a component until all its state exists (Om.Next takes an approach somewhat like this). 

You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscribe@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
“One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.”
(Robert Firth)

Deon Moolman

unread,
Sep 12, 2016, 4:30:00 AM9/12/16
to clo...@googlegroups.com
Hi Tim,

Thanks for your feedback, I appreciate it! :)

Ye, I've been tempted and bitten by atoms-in-atoms before, and that's no fun - My case here is slightly different though, I have a single atom and construct cursors to paths within, which are swappable and deref'able like normal atoms, but they modify the value of the original atom (just their allocated slice inside though).

I'll give a bit of detail on one very specific usecase I have, my structure looks like so:

My atom (and its value) looks like so:

(atom
  {:product
   [{:product/id 1 :product/name "Cooker"}
    {:product/id 2 :product/name "Pan"}]
   :image
   [{:image/id 1 :image/url "..." :image/product 1}]})

And I have a function (reverse-lookup) that will take a product id and return a list of cursors that point to the individual images for a product, so:

(reverse-lookup :image/product 1) => [(cursor {:image/id :image/url "..." :image/product 1})]
(reverse-lookup :image/product 2) => []

If I'm only interested in, say, rendering the first image, I can call (render-image @(first product-image-list)) - without nil-punning, I have to do add an if to every usage, where I'd rather just handle nil to a default image placeholder inside render-image.

I also have a function will literally return the first (and likely only) element in the lookup, which will also return a cursor or nil if not found. One could argue that the data needs integrity checking in that case before getting into the atom, but in my case I'd rather just nil-pun all the things since it's not exactly a big deal.

Anyhow - back to the original question - I'm wondering if there's anything I'm missing about the decision to not nil-pun deref - If it's simply a matter of 'didn't really think of it', that's fine, I'm quite content to work around it, but if there's a deeper philosophical reason, then I'm keen to add it to my understanding of the underlying foundation of clojure.

Thanks again for all the feedback, I really do appreciate your time!

Cheers,
 - Deon

Tim Gilbert

unread,
Sep 15, 2016, 1:37:19 PM9/15/16
to Clojure
In this specific case, I might personally use something like:

(render-image (some-> product-image-list first deref))

...or maybe write a little function that does the above. Alternately, in (render-image) you might start out with (if (nil? cursor) (default-image) (code-to-render @cursor)). You'd need to check for nil to render the default image even if deref was nil-punnable, right?

Tim

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/ot_sD2sJW0A/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
“One of the main causes of the fall of the Roman Empire was that–lacking zero–they had no way to indicate successful termination of their C programs.”
(Robert Firth)

--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to

For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to a topic in the Google Groups "Clojure" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clojure/ot_sD2sJW0A/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clojure+u...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages