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]]))
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.
I was (probably) supposing that isa? would return false if given objects that don’t represent hierarchies. Apparently that supposition was wrong.
This code was written several years ago, so I don’t anymore remember exactly what I was thinking at the time.
The behavior you describe is consistent with the docs. The docs do not mention any occasion when isa? must return false. 🙂
the docstring explicitly states that isa? returns true if its arguments are =
@phill That observation suggests an excellent performance improvement.
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.
this rule is still good advice if you move the cutoff time back 5 hours!
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
I’ve discovered that (list? (cons 'a '(1 2 3))) returns false.
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 misnomersyntax quoted 'lists' also probably don't behave like you'd expect:
(list? `()) ;=> true
(list? `(1)) ;=> true
(list? `(1 2)) ;=> falseIt's almost never worth it to use list?. In 99.99% of the cases, use seq?
yeah, cons is one of the parts of lazy sequences, so it's not a list, it's a Cons
There are many surprises of this kind for people with previous lisp experience 🤷♂️
interesting.. it is surprising that cons and conj return different types when consing onto a list.
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))
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.
Or check (seq x) which returns nil for an empty sequence.
It doesn’t fit into my brain that () and nil are different
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)))))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))))
seq will turn a seqable? into a seq or nil if it's empty.
list? is broken, it's a famous footgun.
possibly the worst left since they fixed contains?
How did they fix contains??
@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.
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)).
@weavejester (contains? '(1) 1) used to say false, now it's an error