Welcome! Please see the About page for a little more info on how this works.

0 votes
in Clojure by

It is not possible to use case with a single empty seq of options, or with a single seq of options and a default clause.

I would expect
bq. (case 1 () :a :none)

to return :none, instead it fails with an uninformative exception: "Unhandled clojure.lang.ArityException: Wrong number of args (-2) passed to: core/max"

I would expect (case 1 () :a) to fail with "java.lang.IllegalArgumentException: No matching clause", but instead it also fails with
"Unhandled clojure.lang.ArityException: Wrong number of args (-2) passed to: core/max"

This seems inconsistent, as passing an empty list of options is fine when there are other alternatives:

bq. (case 1 () :a 2 :b :none)

returns :none, as expected

The attached patch removes the test-clause pairs with empty test lists before further conversion to case**, and adds tests.

8 Answers

0 votes
by

Comment made by: chrisblom

oops, typo in the title (duplicated "single")

0 votes
by

Comment made by: alexmiller

An empty match list in case seems like it should be an error - maybe this should fail to compile rather than ignoring?

0 votes
by

Comment made by: chrisblom

An empty list of options is currently supported when multiple clauses are given, so failing to compile on empty lists would be a breaking change.

This works in 1.8:

(case 1
() :never-happens
1 :ok
:default)
=> :ok

But this does not:

(case 1
() :never-happens
:default)
=> throws clojure.lang.ArityException: Wrong number of args (-2) passed to: core/max

Only failing when no other clauses are given seems very inconsistent to me.

0 votes
by

Comment made by: bronsa

an empty list doesn't make any sense in case as the correct way to match a literal empty list is (case () (()) :empty). I don't see any value in making it not throw and my vote is to have the case macro complain at compile time every time a () ise used

0 votes
by

Comment made by: alexmiller

() would never match anything now, so failing on () would not break any existing case that matches.

The one case I can imagine might exist though is a macro that creates a case and could potentially programmatically create an empty case list? Something like this:

(defmacro make-case [xs] `(defn ~'foo [e#] (case e# ~xs "matched" "nope")))

0 votes
by

Comment made by: chrisblom

I'm not using this to match empty lists, I ran into this corner case when generating case statements from a DSL.
While it is a pathological case, I disagree that is does not make any sense, an empty list here simply represents no alternatives,
so the clause wil never match and its result-expr will never run.

My point is that now (in clojure 1.8) this is allowed:

(case a
() :never-happens
1 :a
2 :b
:default)

it is equivalent to (as an empty list never matches)

(case a
1 :a
2 :b
:default)

But

(case a
() :never-happens
:default)

gives an uninformative error.

I argue that it should be equivalent to

(case a
:default)

as rejecting empty lists in case statements in general would be a breaking change,
and only rejecting empty lists when no other clauses are present is inconsistent.

0 votes
by

Comment made by: chrisblom

??() would never match anything now, so failing on () would not break any existing case that matches.??

As () in case is allowed in clojure =<1.8 (just not when its the only clause), letting the compiler reject it would potentially break existing code.

??The one case I can imagine might exist though is a macro that creates a case and could potentially programmatically create an empty case list???

That is exactly how i ran into this problem

0 votes
by
Reference: https://clojure.atlassian.net/browse/CLJ-2164 (reported by chrisblom)
...