On Dec 17, 2012, at 12:39 PM, Ben Wolfson wrote:

> On Mon, Dec 17, 2012 at 9:32 AM, Chas Emerick <c...@cemerick.com> wrote:
>> 
>> 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):
> 
> Also as long as you don't want to return a function from the multimethod, no?

Correct.  As noted in the docs for `trampoline`:

        ...if you want to return a fn as a final value, you must wrap it in 
some data structure and unpack it after trampoline returns.

I can't say I've ever needed to write mutually-recursive higher-order 
functions.  I can only presume that doing so while needing to meet trampoline's 
contract for that corner case would be fairly cumbersome.

IIRC, `trampoline` predated functions supporting metadata by some time.  If it 
were written again now, it might specify that metadata should be used to 
indicate that a function should be called-through, rather than returned as a 
non-intermediate value.  e.g.:

(defn meta-trampoline
  ([f]
     (let [ret (f)]
       (if (and (fn? ret) (-> ret meta :trampoline))
         (recur ret)
         ret)))
  ([f & args]
     (meta-trampoline #(apply f args))))

With this, you can return whatever you want/need to, as long as you mark the 
self- or mutually-recursive calls with {:trampoline true} metadata:

=> (defmethod foo String
     [x]
     #(str "I got " x ", you gave me " %))
#<MultiFn clojure.lang.MultiFn@4f0ab3f2>
=> (defmethod foo Long
     [x]
     ^:trampoline #(foo (str x)))
#<MultiFn clojure.lang.MultiFn@4f0ab3f2>
=> (meta-trampoline foo 5)
#<user$eval1992$fn__1993$fn__1994 user$eval1992$fn__1993$fn__1994@7466a008>
=> (*1 6)
"I got 5, you gave me 6"

Cheers,

- Chas

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