Clojure

Recursive function raises "call unbound fn" exception

Details

  • Type: Defect Defect
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Declined
  • Affects Version/s: Release 1.4
  • Fix Version/s: None
  • Component/s: None
  • Labels:
    None
  • Environment:
    OSX

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.

Activity

Hide
Kevin Downey added a comment -

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).

Show
Kevin Downey added a comment - 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).
Hide
Kevin Downey added a comment -

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

Show
Kevin Downey added a comment - meta comment: I would close this as "Declined", but I am not sure if that is kosher for me to do.
Hide
Armando Blancas added a comment -

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.

Show
Armando Blancas added a comment - 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.
Hide
Kevin Downey added a comment -

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.

Show
Kevin Downey added a comment - 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.
Hide
Armando Blancas added a comment -

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.

Show
Armando Blancas added a comment - 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.
Hide
Stuart Sierra added a comment -

Closed based on discussion in comments.

Show
Stuart Sierra added a comment - Closed based on discussion in comments.
Stuart Sierra made changes -
Field Original Value New Value
Resolution Declined [ 2 ]
Status Open [ 1 ] Resolved [ 5 ]
Stuart Halloway made changes -
Status Resolved [ 5 ] Closed [ 6 ]

People

Vote (0)
Watch (0)

Dates

  • Created:
    Updated:
    Resolved: