Arc Forumnew | comments | leaders | submitlogin
2 points by Pauan 5159 days ago | link | parent

I think so. The Arc source for "rfn" just uses "let" to initialize the variable to nil, then assigns a function to the variable. I'll try and see if I can come up with a simple definition of "let" that works like letrec. I think it would be great if Arc could have a simple unified "let", rather than multiple similar-but-slightly-different forms.


3 points by rocketnia 5158 days ago | link

I like it just the way it is in this regard. If (let x (+ 1 x) ...) can get the 'x from the outer scope, then (let x (fn () x) ...) should be able to get that 'x too. Besides, I like minimizing the number of times I type a local variable name; the name 'self is often much shorter than the name of my function, and I have less editing to do if I decide to rename it. (I can find-and-replace, but that leaves me with indentation to fix.)

At one point I wanted to unify all lets to be Haskell-style, so that you could do crazy things like building arbitrary cyclic data structures:

  (let x (cons 1 y)
       y (cons 0 x)
    (iso (firstn 4 x) '(1 0 1 0)))
After all, recursive functions are cyclic data structures in a sense.

But now I think that's a job for a more pure language; as far as I can tell, it requires partial evaluation and lazy (or at least non-strict) semantics. I'm sure I'm wrong, 'cause strict Haskells exist, but I'm not sure what they do here.

Here's my comment from a similar thread that came up a while ago, which might help if you're looking for a 'letrec implementation: http://arclanguage.org/item?id=12418

-----

1 point by aw 5158 days ago | link

The key point is that in

  (some-let foo (... foo ...)
sometimes you might want the reference to "foo" to refer to the foo you are currently defining, or to the foo in the enclosing scope.

There are a couple ways to do this.

One way is that you can use different names for some-let to indicate which one you want.

For example, you could have "let" mean to use foo from the enclosing scope (which is what Arc does now), and have "letrec" mean to use the foo that you're defining.

Or, if you prefer, you could have "let" mean to use the foo that you're defining (and so would work like what we've been calling letrec), and make up another name ("let-parent" or something) for the other kind of let which works like Arc's let does now. You might prefer this if you use the former more often than the latter.

Choosing different names for let isn't the only way to distinguish between the two possibilities. You could modify the Arc compiler so that let worked the way it does now, except that you add a syntax to get at the foo being defined:

  (let rec (fn (x) (this.rec x)))
or, if you wanted let to work like letrec by default instead, except that you needed some way of sometimes referring to the enclosing foo, you could create a syntax for that:

  (let x (+ 1 parent.x) ...)

-----

1 point by Pauan 5158 days ago | link

Yes, that's what I'm saying. I understand now that sometimes it is useful to pull in a variable from an outer scope, but sometimes you want the behavior of letrec. I'm pretty sure letrec would be trivial to write as a macro, so this discussion is more about which should be the default. Do people usually want the outer scope, or the inner?

P.S. I whipped up a quick version of letr; it seems to work okay:

  (mac letr (var val . body)
    `(let ,var nil
       (assign ,var ,val)
       ,@body))
P.P.S. Adding more syntax would also solve it, but I actually would prefer to give them two different names. It just seems cleaner and less cluttered to me.

-----

2 points by akkartik 5158 days ago | link

That makes sense. But why is:

  (letr rec (fn (x) (rec x))
    (rec 3))
superior to

  (let rec (afn (x) (self x))
    (rec 3))

?

-----

1 point by Pauan 5158 days ago | link

For me, two reasons:

1) afn is somewhat of an anomaly. When I see "fn" I know "this is a function" but when I see "afn" I have to pause for a split second to realize what it does.

2) Pattern recognition due to redundancy. When I glance at the first version, my eyes quickly notice that there are 3 references to "rec", and thus I immediately form a pattern in my mind. But in the second version, there are two references to the same function: self and rec. This adds an additional cognitive overhead for me.

In other words, the first one appears less cluttered to me because it has fewer unique elements, and because "fn" is more familiar to me than "afn".

It's not a huge deal, but I would like to reduce the cognitive overhead of reading and writing code as much as I can, which is one of the things I really like about Arc: it's clean, simple, and short, but allow tons of flexibility to change the language to suit the way you think.

-----

1 point by akkartik 5158 days ago | link

"You could modify the Arc compiler so that let worked the way it does now, except that you add a syntax to get at the foo being defined"

I was thinking along these lines as well. How about:

  (let (def rec(x) (rec x))
    (rec x))

-----