Very helpful. Thanks so much! 2012/12/18 Chas Emerick <c...@cemerick.com>
> 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 > -- 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