Mysterious errors w/ protocols and types

8 views
Skip to first unread message

samppi

unread,
Nov 23, 2009, 1:36:46 AM11/23/09
to Clojure
The following code contains an error, and I cannot figure out what it
is at all. When I run StateMeta's test once, the statement marked with
a comment above fails. When I run it again, it succeeds. This has been
causing very weird bugs in my code. I've replicated the behavior in
both a script and the REPL. Here's the bugged code:

(ns name.choi.joshua.fnparse
[:use clojure.test])

(defprotocol ABankable
(get-bank [o])
(vary-bank [o f & args]))

(with-test
(deftype StateMeta [bank index rule-stack]
[clojure.lang.IPersistentMap])
(let [bank {:a 3}
state-meta (StateMeta bank nil nil)]
(is (= (get-bank state-meta) bank))
(is (= (get-bank (vary-bank state-meta identity)) bank)) ; This is
the statement throwing an exception
(is (= (get-bank (vary-bank state-meta assoc :b 2))
(assoc bank :b 2)))))


(extend ::StateMeta ABankable
{:get-bank :bank
:vary-bank (fn vary-state-meta-bank [this f & args]
(println ">>>>>>>>>" this f args)
(apply update-in this [:bank] f args))})

---

Here is the behavior in a REPL by pasting in that code:

Clojure 1.1.0-alpha-SNAPSHOT
user=> (ns name.choi.joshua.fnparse
[:use clojure.test])

(defprotocol ABankable
(get-bank [o])
(vary-bank [o f & args]))

(with-test
(deftype StateMeta [bank index rule-stack]
[clojure.lang.IPersistentMap])
(let [bank {:a 3}
state-meta (StateMeta bank nil nil)]
(is (= (get-bank state-meta) bank))
(is (= (get-bank (vary-bank state-meta identity)) bank))
(is (= (get-bank (vary-bank state-meta assoc :b 2))
(assoc bank :b 2)))))


(extend ::StateMeta ABankable
{:get-bank :bank
:vary-bank (fn vary-state-meta-bank [this f & args]
(println ">>>>>>>>>" this f args)
(apply update-in this [:bank] f args))})

nil
name.choi.joshua.fnparse=> name.choi.joshua.fnparse=> ABankable
name.choi.joshua.fnparse=> name.choi.joshua.fnparse=>
#'name.choi.joshua.fnparse/StateMeta
name.choi.joshua.fnparse=> name.choi.joshua.fnparse=>
name.choi.joshua.fnparse=> nil
name.choi.joshua.fnparse=> name.choi.joshua.fnparse=> (test
#'StateMeta)

ERROR in clojure.lang.PersistentList$EmptyList@1 (NO_SOURCE_FILE:13)
expected: (= (get-bank (vary-bank state-meta identity)) bank)
actual: java.lang.IllegalArgumentException: Wrong number of args
passed to: fnparse$eval--21$fn--33$G--9
at clojure.lang.AFn.throwArity (AFn.java:449)
clojure.lang.AFn.invoke (AFn.java:56)
name.choi.joshua.fnparse$eval__54$fn__84$fn__91.invoke
(NO_SOURCE_FILE:13)
name.choi.joshua.fnparse$eval__54$fn__84.invoke (NO_SOURCE_FILE:
13)
clojure.core/test (core.clj:3125)
name.choi.joshua.fnparse/eval (NO_SOURCE_FILE:24)
clojure.lang.Compiler.eval (Compiler.java:4939)
clojure.lang.Compiler.eval (Compiler.java:4907)
clojure.core/eval (core.clj:1975)
clojure.main$repl__7993$read_eval_print__8005.invoke (main.clj:
180)
clojure.main$repl__7993.doInvoke (main.clj:197)
clojure.lang.RestFn.invoke (RestFn.java:422)
clojure.main/repl_opt (main.clj:251)
clojure.main/legacy_repl (main.clj:292)
clojure.lang.Var.invoke (Var.java:365)
clojure.main.legacy_repl (main.java:27)
clojure.lang.Repl.main (Repl.java:20)
>>>>>>>>> #:StateMeta{:bank {:a 3}, :index nil, :rule-stack nil} #<core$assoc__4564 clojure.core$assoc__4564@1a8773c> (:b 2)
:ok
name.choi.joshua.fnparse=> (test #'StateMeta)
>>>>>>>>> #:StateMeta{:bank {:a 3}, :index nil, :rule-stack nil} #<core$identity__5033 clojure.core$identity__5033@18d7ace> nil
>>>>>>>>> #:StateMeta{:bank {:a 3}, :index nil, :rule-stack nil} #<core$assoc__4564 clojure.core$assoc__4564@1a8773c> (:b 2)
:ok

---

This is absolutely stupefying. What in the world could it be?

Krukow

unread,
Nov 23, 2009, 9:59:53 AM11/23/09
to Clojure


On Nov 23, 7:36 am, samppi <rbysam...@gmail.com> wrote:
> The following code contains an error, and I cannot figure out what it
> is at all. When I run StateMeta's test once, the statement marked with
> a comment above fails. When I run it again, it succeeds. This has been
> causing very weird bugs in my code. I've replicated the behavior in
> both a script and the REPL. Here's the bugged code:
>
[snip]

I recommend that you try and find a minimal version of a program that
exhibits the bug. This makes it so much easier to fix quickly. In your
case, it looks like the combination of varargs and protocol functions:

krukow:~/emacs/clojure/clojure$ rlwrap java -cp
clojure-1.1.0-75cd05080f7260c54007d7728fb280ae53b56f63.jar
clojure.main
Clojure 1.1.0-alpha-SNAPSHOT
user=> (defprotocol P (foo [x & args]))
P
user=> (deftype T [x])
#'user/T
user=> (extend ::T P {:foo (fn [x & args] args)})
nil
user=> (foo (T 42) 1 2 3)
java.lang.IllegalArgumentException: Wrong number of args passed to:
user$eval--12$fn--14$G--1 (NO_SOURCE_FILE:0)
user=>

Perhaps varargs isn't supported yet?

/Karl

samppi

unread,
Nov 23, 2009, 11:36:57 AM11/23/09
to Clojure
Variable-length arguments in protocols seem to be supported, but
there's just a weird, stateful behavior. Look what happens when you
call foo first with one argument twice (it fails both times), then two
arguments (it succeeds), then one argument again (it succeeds now
too!). Is this a Clojure bug?

Clojure 1.1.0-alpha-SNAPSHOT
user=> (defprotocol P (foo [x & args]))
P
user=> (deftype T [x])
#'user/T
user=> (extend ::T P {:foo (fn [x & args] args)})
nil
user=> (foo (T 42))
java.lang.IllegalArgumentException: Wrong number of args passed to:
user$eval--12$fn--14$G--1 (NO_SOURCE_FILE:0)
user=> (foo (T 42))
java.lang.IllegalArgumentException: Wrong number of args passed to:
user$eval--12$fn--14$G--1 (NO_SOURCE_FILE:0)
user=> (foo (T 42) 1 2)
(1 2)
user=> (foo (T 42))
nil

Rich Hickey

unread,
Nov 23, 2009, 2:24:22 PM11/23/09
to Clojure
Varargs in protocols are not yet supported.

Rich

samppi

unread,
Nov 23, 2009, 2:39:07 PM11/23/09
to Clojure
I see; then that of course would be the reason. Thanks for the answer.
Reply all
Reply to author
Forward
0 new messages