clojure

Jim Newton 2025-12-05T08:55:29.455699Z

hmmm, I found a bug in my code which I don’t understand. somehow isa? is getting called on objects that are not in any way (that I can envision) derived from a java type relationship, and isa? is returning `true. (isa? '(member [1 2 (3 4)]) '(member [1 2 [3 4]]))

Jim Newton 2025-12-05T08:56:16.545409Z

looking into the code in clojure.core it seems that isa? returns true if the two given objects are = even if they don’t represent any hierarchy at all.

Jim Newton 2025-12-05T08:58:39.562369Z

I was (probably) supposing that isa? would return false if given objects that don’t represent hierarchies. Apparently that supposition was wrong.

Jim Newton 2025-12-05T08:59:00.037329Z

This code was written several years ago, so I don’t anymore remember exactly what I was thinking at the time.

2025-12-05T09:00:34.014019Z

The behavior you describe is consistent with the docs. The docs do not mention any occasion when isa? must return false. 🙂

Jim Newton 2025-12-05T09:01:35.984999Z

the docstring explicitly states that isa? returns true if its arguments are =

2025-12-05T11:12:56.487539Z

@phill That observation suggests an excellent performance improvement.

😀 1
Jim Newton 2025-12-05T09:40:49.865759Z

I have a list of rules of thumbs of programming that I’ve amassed over the years. I’m adding a new rule to this list. rule 4: If you discover a new bug at midnight, don’t try to fix it now--wait until tomorrow.

💯 6
💡 1
respatialized 2025-12-05T15:39:01.909949Z

this rule is still good advice if you move the cutoff time back 5 hours!

2025-12-05T20:12:29.912029Z

I do have to at least write down everything I know about the bug to make sure that tomorrow-me has all the relevant info

Jim Newton 2025-12-05T13:46:17.511259Z

I’ve discovered that (list? (cons 'a '(1 2 3))) returns false.

yuhan 2025-12-07T11:05:06.188759Z

This in particular was quite a surprise when I first found out:

(list? (list* 1 2 [3])) ;=> false
re. surely one of the two be a misnomer

yuhan 2025-12-07T11:07:36.846479Z

syntax quoted 'lists' also probably don't behave like you'd expect:

(list? `()) ;=> true
(list? `(1)) ;=> true
(list? `(1 2)) ;=> false

😂 2
borkdude 2025-12-05T13:52:47.550899Z

It's almost never worth it to use list?. In 99.99% of the cases, use seq?

✔️ 1
✅ 3
2025-12-05T14:09:18.893039Z

yeah, cons is one of the parts of lazy sequences, so it's not a list, it's a Cons

2025-12-05T14:28:51.874149Z

There are many surprises of this kind for people with previous lisp experience 🤷‍♂️

Jim Newton 2025-12-05T15:45:55.438549Z

interesting.. it is surprising that cons and conj return different types when consing onto a list.

Jim Newton 2025-12-05T15:47:16.385109Z

the other thing that gets me which I always re-forget again and again. the tail of a singleton list is not falsey. (rest '(1))

seancorfield 2025-12-05T16:29:48.462329Z

list? and seq? are very "concrete" in terms of when they return true. You probably want sequential? or perhaps seqable? instead. Re: rest -- (next '(1)) returns nil (falsey). That's the big difference between next and rest.

seancorfield 2025-12-05T16:30:18.082989Z

Or check (seq x) which returns nil for an empty sequence.

Jim Newton 2025-12-05T17:58:13.908249Z

It doesn’t fit into my brain that () and nil are different

Jim Newton 2025-12-05T17:59:39.249549Z

I didn’t know about next. So I guess code like the following should use next instead of rest

(loop [as a
                   bs b]
              (cond (and (empty? as)
                         (empty? bs))
                    true

                    (or (empty? as)
                        (empty? bs))
                    false

                    :otherwise
                    (and (strong-equal? (first as) (first bs))
                         (recur (rest as) (rest bs)))))

weavejester 2025-12-05T18:52:10.737159Z

I'd probably write it as:

(loop [as (seq a), bs (seq b)]
  (if (and as bs)
    (and (strong-equal? (first as) (first bs))
         (recur (next as) (next bs)))
    (and (nil? as) (nil? bs))))

weavejester 2025-12-05T18:53:26.370219Z

seq will turn a seqable? into a seq or nil if it's empty.

technomancy 2025-12-05T18:58:26.017169Z

list? is broken, it's a famous footgun.

technomancy 2025-12-05T18:58:58.579189Z

possibly the worst left since they fixed contains?

weavejester 2025-12-05T19:00:38.180299Z

How did they fix contains??

Jim Newton 2025-12-05T19:23:07.525259Z

@weavejester that type of nil-punning coding scares me. I’m not confident enough to understand which types of objects have a gotcha that an empty object is truthy. so I always use empty? and not-empty …. if I use those functions then I know i’m never going to be hit with the empty-truthy object lying in wait.

weavejester 2025-12-05T19:28:02.733919Z

The rule in Clojure is universal and fairly straightforward: nil and false are falsey; everything else is truthy. When dealing with iterating over collections using loop/recur, it's often useful to convert them into seqs first using seq. This will return a seq or nil if its empty. Similarly, (next x) is equivalent to (seq (rest x)).

technomancy 2025-12-05T19:40:08.663419Z

@weavejester (contains? '(1) 1) used to say false, now it's an error

😵‍💫 1
😆 1