Arc Forumnew | comments | leaders | submit | akkartik's commentslogin
4 points by akkartik 13 days ago | link | parent | on: Recursive anonymous functions?

Is that the Y combinator? I don't think I've seen it ever used "for real". It's not in either Arc 3.1 or Anarki.

You aren't using bracket notation as you originally asked. Might as well just use afn. It can call itself recursively as self. http://arclanguage.github.io/ref/anaphoric.html

reply

3 points by waterhouse 9 days ago | link

The Y combinator itself is more cumbersome, having an extra currying step or two. I prefer the form hjek is using—which is a function that expects to take "itself" as an extra parameter, like this:

  (fn (f i)
    (aif i!parent
         (+ 1 (f f (item it)))
         0))
So the recursive call, "(<self> (item it))", is implemented as "(f f (item it))". And then usage is very simple: actually give it itself as an extra argument.

The Y combinator works with a different function signature:

  (fn (f)
    (fn (i)
      (aif i!parent
           (+ 1 (f (item it)))
           0)))
That is, the function takes "something that's not quite itself" as an argument, and returns a function which does one step of computation and may do a "recursive" call using the thing that was passed into it. The implementation would therefore like to be:

  (def fix (f) ;aka Y
    (fn (i)
      ((f (fix f)) i)))
But, if we're doing the entire thing with anonymous recursion, we can (laboriously) implement fix like this:

  (= fix ;aka Y
     (fn (f)
       ((fn (g) (g g))
        (fn (g)
          (fn (i)
            ((f (g g)) i))))))
Every recursion step involves creating multiple lambdas. Eek. (It's even worse if you use the general, n-argument Y combinator, in which case you must use "apply" and create lists.) Whereas with hjek's non-curried approach, only a constant number of lambdas have to be created at runtime. (Optimizing compilers might be able to cut it down to 0.)

If you want to create a macro like afn or rfn, and want the user to be able to act like the function is named F and accepts just the parameter i, you can put a wrapper into the macroexpansion, like this:

  (rfn F (i)
    (aif i!parent
         (+ 1 (F (item it)))
         0))
  ->
  (fn (i)
    ((fn (f)
       (f f i))
     (fn (f i)
       (let F (fn (i) (f f i))
         (aif i!parent
              (+ 1 (F (item it)))
              0)))))
And in this case, while the code does call for creating an F-lambda on every recursive call, I think it's easier for the compiler to eliminate it—I don't remember whether I'd gotten Racket to do it. (I think it probably did eliminate it when working with Racket code, but Arc, which generates all the ar-funcall expressions, might not have allowed that.)

The actual code for rfn will create a variable and then modify it, creating a lexical environment with a cycle in it. That's certainly a more straightforward approach. I figure the above is useful only if you're working in a context where you really want to avoid mutation or true cycles. (For example, I am considering a system that detects macros whose expansion is completely side-effect-free. It might be easier to use the above approach to defining iteration than to teach the system that rfn is "close enough" to being side-effect-free.)

reply

2 points by hjek 10 days ago | link

I've been using `aif` and `awhen` a lot but didn't know about `afn`. Thanks!

Is it the Y combinator? I didn't get that far in The Little Schemer yet, but I'll have to check.

reply

2 points by akkartik 10 days ago | link

Doesn't look quite like it, but close.

reply

2 points by akkartik 16 days ago | link | parent | on: Writing a sane list macro

That seems pretty much unhacky :)

reply


Check out John Shutt's thesis on Kernel: https://web.cs.wpi.edu/~jshutt/kernel.html. He takes your observation that quasiquotation is pointless to the limit, dropping it entirely and constructing his macros out of cons and friends.

reply

2 points by waterhouse 9 days ago | link

Thanks for the pointer. He has an entire chapter on hygiene... And then there is this:

"There is no need to provide quotation here because, having failed to enforce the prohibition against embedding combiners in a macro expansion, we don’t need to embed their unevaluated names in the expansion."

It's nice that his primitive $vau grabs the current lexenv, as this enables another kind of macro-like behavior I've thought might be useful: taking subexpressions, fully macroexpanding them, and doing something with the result (e.g. determining whether a variable is ever used, and omitting a computation if not). I don't know how that would mesh with the interpreter's semantics, though...

I'll probably have to read and brood on this further.

reply

3 points by akkartik 25 days ago | link | parent | on: About lobste.rs

I'll invite you.

reply

3 points by zck 24 days ago | link

Thanks! I'll spend some time checking it out. :)

reply

2 points by akkartik 27 days ago | link | parent | on: Self-hosting the Anarki community

Hmm, I wonder if it stays banned until they restart the server. I'd ping hn@ycombinator.com.

reply

2 points by hjek 26 days ago | link

Looks like banned IPs are written to the disk even:

    (def set-ip-ban (user ip yesno (o info))
      (= (banned-ips* ip) (and yesno (list user (seconds) info)))
      (todisk banned-ips*))

reply

4 points by akkartik 34 days ago | link | parent | on: With and withs

I actually tend to the opposite: use with everywhere unless I need withs. The reason isn't performance. It tends to make code more elegant to not rely on the order in which things are defined. And when I'm reading code, with gives me the warm fuzzies that the code is going to be cleaner. When I see withs I slow down to look at the dependencies between the bindings.

reply

3 points by zck 34 days ago | link

Similarly to this, when I'm writing Java, I use `final`^1 everywhere I can. It's nice to be able to know that anywhere later where the variable declared final is in scope, it will have the same value as at the point it's set. I don't need to look through any code to see if it's rebound; I know it hasn't been.

[1] "final" is kind of like "const", if I understand `const` right. `final int x = 3;` means that it is an error to later have the line of code `x = 4;`.

reply

3 points by prestonbriggs 34 days ago | link

OK, I get it, thanks. In scheme, I would use letrec for this situation; my intuition for Arc isn't very well developed.

reply

3 points by akkartik 34 days ago | link | parent | on: With and withs

Try running without your code.

    arc> (help get)
    [fn]  (get i)
    Returns a function to pass 'i' to its input.
    Useful in higher-order functions, or to index into lists, strings, tables, etc.
    Examples:
      arc> (get.2 '(1 2 3 4))
      3
      arc> (get!b (obj a 10 b 20))
      20
      arc> (get.9 sqrt)
      3
      arc> (map get.2
                '((a b c)
                  (1 2 3)
                  (p q r)))
      (c 3 r)
    nil

    arc> (help set)
    [mac] (set . args)
    Sets each place in 'args' to t.
    nil
These are the functions you end up calling because your dispatch can't see the earlier get and set bindings.

reply

2 points by akkartik 36 days ago | link | parent | on: Self-hosting the Anarki community

I did ping the HN admins about the lock period a year or two ago, and they were kind enough to extend it for us. It's now 90 days, if I recall correctly.

reply


Looking forward to seeing what use you put user-defined unquote macros to!

reply

2 points by rocketnia 37 days ago | link

Thanks! :)

reply

2 points by akkartik 37 days ago | link | parent | on: Advanced search for news.arc

Huh, surprising to me that I'm listed as an owner of the arc organization on notabug.org! Did I click on something without realizing what I was doing? I don't see any email from notabug.org about the new organization in my email or trash..

Sorry, it seems I'm not wholly attentive lately.

reply

2 points by hjek 37 days ago | link

I clicked Invite but it must have just added you?

reply

1 point by akkartik 37 days ago | link

Do you happen to remember how long ago this was? If it was more than 30 days ago I may well have accepted and forgotten, and see no trace of it in my email's trash.

Adding people to organizations without informing them seems like a bad idea. It's also not what GitHub does, and GitHub is who all these sites are copying, so shouldn't be happening.

reply

More