<< Back to previous view

[CLJ-1051] Recursive function raises "call unbound fn" exception Created: 27/Aug/12  Updated: 01/Mar/13  Resolved: 09/Nov/12

Status: Closed
Project: Clojure
Component/s: None
Affects Version/s: Release 1.4
Fix Version/s: None

Type: Defect Priority: Major
Reporter: Armando Blancas Assignee: Unassigned
Resolution: Declined Votes: 0
Labels: None
Environment:

OSX


Attachments: File expr.clj    

 Description   

I've tried to reduce the code to the minimum that will reproduce the problem. This is a parser combinator library that uses the "list of successes" method. The test is a simple expression for adding one-digit integers with support for parens; sample input might be "1+(2+3)+4".

Having declared the parsers (see attached file), the expression parser is this:

(declare expr)

(def factor
  (choice (between (match \() (match \)) expr)
	  integer))

(def expr
  (bind factor
       (fn [t]
	 (choice (bind (match \+) (fn [_] (bind expr (fn [e] (result (+ t e))))))
	         (result t)))))

With the above, I get this error:

Clojure 1.4.0
user=> (load-file "expr.clj")
#'user/expr
user=> (run expr "(3)")      
IllegalStateException Attempting to call unbound fn: #'user/expr  clojure.lang.Var$Unbound.throwArity (Var.java:43)

I can, however, avoid this error if I code the (factor) function by expanding the code of the parser (between):

(declare expr)

(def factor*
  (choice (bind (match \() (fn [_]
          (bind expr       (fn [e]
	  (bind (match \)) (fn [_] (result e)))))))
	  integer))

(def expr
  (bind factor*
       (fn [t]
	 (choice (bind (match \+) (fn [_] (bind expr (fn [e] (result (+ t e))))))
	         (result t)))))

And now I can do:

Clojure 1.4.0
user=> (load-file "expr.clj")
#'user/expr
user=> (run expr "(3)")      
3
user=> (run expr "1+(2+3)+(4+5)")
15

The ability to reuse parser and add conveniences like (between) is key for this style of parsing.



 Comments   
Comment by Kevin Downey [ 27/Aug/12 6:03 PM ]

this is not a bug in clojure.

declare creates an unbound var then your def tries to use the value of the var before you have given it a value.

declare does not let you use a value of a var before you have given it one (via def or whatever).

Comment by Kevin Downey [ 27/Aug/12 6:05 PM ]

meta comment:
I would close this as "Declined", but I am not sure if that is kosher for me to do.

Comment by Armando Blancas [ 27/Aug/12 10:28 PM ]

Thanks for looking into this.

Just want to point out that as far as the declare and the use of expr as a forward reference, the second scenario I've presented (with factor*) uses (declare) the same way, yet it works: the var "expr" in (factor*) ends up pointing to the root value set later, but before it runs.

Seems to me similar to this case, where f is defined in terms of itself and it works fine, having the var "f" obtained its root binding after the def form runs:

user=> (def f (fn [n] (if (< n 1) 1 (* n (f (- n 1))))))
#'user/f
user=> (f 6)
720

Given that I've got a usage that works, I would suspect that there's something about the first case that prevents the root binding to be visible to the declared var.

Comment by Kevin Downey [ 28/Aug/12 3:25 AM ]

the formatting of the second case makes it hard to read, but it looks like expr is referenced inside a function, so the var #'expr will not be derefed until the function is run.

Comment by Armando Blancas [ 28/Aug/12 12:32 PM ]

Though the difference isn't quite apparent to me, I kind of grasp the idea that the var may be in one case deref'ed earlier in one case than the other because calls to (bind) are eager and in the case of the additional call to (between) the var is inside the function. I'll ask the board for advice on this and this ticket can be closed. Thanks.

Comment by Stuart Sierra [ 09/Nov/12 8:51 AM ]

Closed based on discussion in comments.

Generated at Sat Oct 25 12:32:54 CDT 2014 using JIRA 4.4#649-r158309.