letrec

111 views
Skip to first unread message

Razvan Rotaru

unread,
Dec 14, 2011, 2:09:53 PM12/14/11
to Clojure
Hi,

Is there a reliable implementation of letrec in clojure? Anybody using
it?
I have found a post from 2008, with an implementation which I don't
understand (and it's said to be slow), and which I don't know whether
to trust.(It's also supposed to be slow).

Thanks,
Razvan

Kevin Downey

unread,
Dec 14, 2011, 2:13:52 PM12/14/11
to clo...@googlegroups.com
lazy-seq and letfn should cover anything you would need letrec for

> --
> You received this message because you are subscribed to the Google
> Groups "Clojure" group.
> To post to this group, send email to clo...@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+u...@googlegroups.com
> For more options, visit this group at
> http://groups.google.com/group/clojure?hl=en

--
And what is good, Phaedrus,
And what is not good—
Need we ask anyone to tell us these things?

Razvan Rotaru

unread,
Dec 14, 2011, 2:53:28 PM12/14/11
to Clojure
I don't quite understand why people are saying this. Anyway, It's not
enough for me.

David Nolen

unread,
Dec 14, 2011, 2:56:26 PM12/14/11
to clo...@googlegroups.com
On Wed, Dec 14, 2011 at 2:53 PM, Razvan Rotaru <razvan...@gmail.com> wrote:
I don't quite understand why people are saying this. Anyway, It's not
enough for me.

What can't you solve your problem with what was suggested?

David 

Razvan Rotaru

unread,
Dec 14, 2011, 3:53:23 PM12/14/11
to Clojure
letfn defines functions. I'm just defining some values. The values
contain anonymous functions which need to refer to other values.I know
there are workarounds for this, but this means I must change the
interface.

Razvan

On Dec 14, 9:56 pm, David Nolen <dnolen.li...@gmail.com> wrote:

David Nolen

unread,
Dec 14, 2011, 4:09:34 PM12/14/11
to clo...@googlegroups.com
Do you have a minimal example of what you are trying to do?

Razvan Rotaru

unread,
Dec 14, 2011, 4:37:00 PM12/14/11
to Clojure
Yes. Assuming I have following macros:

(button :id b1 :listener #(...)) => (let [b1 (new JButton)] ...)
(panel [:id p1] (button :id b1 ...) (button :id b2 ...)) => (let [p1
(new JPanel) b1 (button :id b1 ...) b2 (button :id b2 ...)] ...)

How to make the listener in b1 refer to b2?

Razvan

On Dec 14, 11:09 pm, David Nolen <dnolen.li...@gmail.com> wrote:
> Do you have a minimal example of what you are trying to do?
>

Daniel

unread,
Dec 15, 2011, 10:23:30 AM12/15/11
to Clojure
Is 'declare' possibly the missing component here?

Bobby Eickhoff

unread,
Dec 15, 2011, 10:48:54 AM12/15/11
to clo...@googlegroups.com
'declare' wouldn't be good because of the scope of vars.  There's no sense using global (albeit namespaced) variables for what probably only need to be local identifiers.

Nils Bertschinger

unread,
Dec 15, 2011, 11:58:41 AM12/15/11
to Clojure
Hi,

to implement letrec in a language with eager evaluation strategy some
kind of mutability is probably needed. Consider for example a self-
referential definition such as

(let [fibo (lazy-cat [1 1] (map + fibo (rest fibo)))]
(take 10 fibo))

This will not work since fibo is not in scope when the binding is
established. The standard solution would probably be something like
this

(let [fibo (promise)]
(deliver fibo (lazy-cat [1 1] (map + @fibo (rest @fibo))))
(take 10 @fibo))

Not as nice as the original version due to explicit dereferencing, but
workable. As an alternative one could use a macro to expand to this
and use a code walker (or symbol-macros) to automatically include the
@ calls. The following is untested, but should be close:

(defmacro letrec [binding & body]
(let [[var expr] binding
g-var (gensym)]
`(let [~g-var (promise)]
(symbol-macrolet [~var @~g-var]
(deliver ~g-var ~expr)
~@body))))

and voila

(letrec [fibo (lazy-cat [1 1] (map + fibo (rest fibo)))]
(take 10 fibo))

produces (1 1 2 3 5 8 13 21 34 55).

Best,

Nils

Meikel Brandmeyer

unread,
Dec 15, 2011, 12:20:38 PM12/15/11
to clo...@googlegroups.com
Hi,

you'll probably have to rewrite your panel macro, so that it recognizes the button (and label, etc) macros ((= #'button (resolve &env sym)) in 1.3) and extracts the ids from the subform. Then you construct a global let which contains all the individual id namings. Then you group all '...' parts (from your standalone button example) in the body of the let.

Sincerely
Meikel

Meikel Brandmeyer

unread,
Dec 15, 2011, 2:00:44 PM12/15/11
to clo...@googlegroups.com
Hi,

if you always follow the let structure you outlined the following might work. Untested, though. Note, that things work recursively with this approach.

(def ours? #{#'button #'label ...})

(defmacro panel
[{:keys [id]} & components]
(let [[bindings body]
(reduce (fn [[bindings body] component]
(if (and (seq? component)
(ours? (resolve &env (first component))))
(let [[_let local-bindings & local-body]
(macroexpand-1 component)]
[(into bindings local-bindings) (into body local-body)])
; Keep stuff we don't know.
[bindings (conj body component)]))
[[id `(JPanel.)] []]
components)]
`(let ~bindings
~@body
...
~id)))

Sincerely
Meikel

Reply all
Reply to author
Forward
0 new messages