`recur` throws control flow to the nearest `loop` head or fn body, and each 
method is itself a function, so `recur` within a method body will simply jump 
to the start of the method, _not_ tail-call the multimethod.  e.g.:

=> (defmulti foo type)
#'user/foo
=> (defmethod foo Long
     [x]
     (assert (integer? x) (str "x isn't an integer! " (pr-str x)))
     (recur (str x)))
#<MultiFn clojure.lang.MultiFn@4f0ab3f2>
=> (foo 5)
AssertionError Assert failed: x isn't an integer! "4"
(integer? x)  user/eval1831/fn--1832 (NO_SOURCE_FILE:3)

What you're trying to do is really a special case of mutual recursion: because 
Clojure's methods are separate functions, calling back through the multimethod 
(and its dispatch fn) will always consume stack space.  The general solution 
for this is to use `trampoline`, which will continuously call through functions 
returned from calling a function until a non-function value is returned.  This 
would allow you to make your multimethod mutually-recursive, as long as those 
recursive calls are made by returning a function (that the user of the 
multimethod would `trampoline` through):

=> (defmethod foo String
     [x]
     (str "got " x "!"))
#<MultiFn clojure.lang.MultiFn@4f0ab3f2>
=> (defmethod foo Long
     [x]
     #(foo (str x)))
#<MultiFn clojure.lang.MultiFn@4f0ab3f2>
=> (foo 5)
#<user$eval1888$fn__1889$fn__1890 user$eval1888$fn__1889$fn__1890@3852fdeb>
=> (trampoline foo 5)
"got 5!"

>From an API standpoint, you would presumably name the multimethod itself 
>something like `trans*`, and expose only a helper function `trans` that would 
>implicitly trampoline through any functions returned by `trans*`.  There is a 
>cost associated with trampolining isn't zero, but won't swamp actual work as 
>long as the methods are doing something nontrivial anyway.

Another option you might consider is using agents.  I see your methods already 
take a single state argument; this would then make it really easy to lift the 
automata onto an agent (or set of agents).  That state would become the agents' 
state, and "recursive" calls would become a `send` (or `send-off` if you're 
doing IO or other blocking things) to the same agent (e.g. `(send *agent* 
trans)`).  

Cheers,

- Chas

--
http://cemerick.com
[Clojure Programming from O'Reilly](http://www.clojurebook.com)

On Dec 17, 2012, at 11:56 AM, juan.facorro wrote:

> What about recur? 
> 
> It's a special form used for tail call optimizations.
> 
> Juan
> 
> On Monday, December 17, 2012 1:32:31 PM UTC-3, bruce li wrote:
> Hello, everyone.
> 
> I'm currently trying to model an automata using multi-method. So in my code, 
> I have something like:
> 
> (defmethod trans :state
>      [state]
>      ; .......
>      (trans ......))))
> 
> In the last line, the trans will be called with some different state.
> 
> However, it seems that such call still consumes stack and I quickly come to a 
> stack overflow error when the states are reached multiple times.
> 
> I'm wondering if there is some ways to optimize the code.
> 
> Thanks!
> 
> -- 
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clojure@googlegroups.com
> Note that posts from new members are moderated - please be patient with your 
> first post.
> To unsubscribe from this group, send email to
> clojure+unsubscr...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to