Arc Forumnew | comments | leaders | submitlogin

Interesting! Clearly my knowledge of the clojure primitives was extremely vague; I wasn't even aware that they operated on forms rather than function values.

Arc could eliminate the ->/->> dichotomy in another way entirely, using the existing syntax for single-arg fns. The examples at http://clojuredocs.org/clojure_core/clojure.core/-%3E%3E would become (arc-ish pseudocode):

  (-> (range) [map [* _ _] _] [filter even? _] [take 10 _] [reduce + _])
  (-> 5 [+ _ 3] [/ 2 _] [- 1 _])
At the same time, the examples at http://clojuredocs.org/clojure_core/clojure.core/-%3E become:

  (-> "a b c d" upcase [replace _ "A" "X"] [split _ " "] first)
What's more, you could mix first and last because all the arguments are actual values rather than hacky s-exprs. I already used that in the second example above, if you look closely.

---

The one missing use case is this:

  (-> animals 'dog 'thaddeus 'age)
Arc just has a different, retro-email solution for that:

  animals!dog!thaddeus!age
Though it all unravels if one of the keys is a string. Ok, we could use some help here. But it feels like a separate mechanism; I would avoid mixing `(,form ,x) and `(,x ,form) in different paths of a single macro.

---

Hmm, even this isn't too bad:

  (-> animals [_ 'dog] [_ 'thaddeus] [_ 'age])
You don't save typing compared to:

  (((animals 'dog) 'thaddeus) 'age)
But the matching parens are closer together and so easier to read.

"Or just use Factor. :)"

I love how I could switch programming models for the one line in this example where it made sense: http://rosettacode.org/wiki/Rot-13#Wart

3 points by thaddeus 4783 days ago | link | parent | on: Broken pipe errors with the arc server

Yes, you'll get broken pipe errors if the browser closes the connection and stops reading prior to completion. I'm guessing some of your users are probably reloading/leaving due to these long response times.

Yeah, if I had to choose one axis over the other I'd rather have multiple functions than multiple arguments.

Here's the current varargs version of -> in wart:

  mac (transform x ... fs|thru)
    if no.fs
      x
      `(transform (,x -> ,car.fs) ,@cdr.fs)
Now you can call it like this:

  (transform 3 :thru (fn(_) (_ * 2))
                     (fn(_) (_ + 1)))
I think I'd support multiple args by using :thru as a delimiter:

  (transform 1 2 :thru (fn(a b) (a * b))
                       (fn(_) (_ + 1)))
But really, there's no need:

  (transform 1*2 :thru (fn(_) (_ + 1)))

The Clojure operator '->', as I know it, is called 'thread-first'. And in Clojure it used to inject a value (as the first argument) into a provided expression. It then returns a result which will get piped through, in the same manner, to any additional expressions supplied. There is also a thread-last '->>' which obviously injects the value as the last argument. It looks like you've decided that thread-last is more useful in Arc (which I would agree with) and have chosen the '->' operator.

I think that's a great idea.

For interest sake here are my conversions of thread-first and thread-last respecting Clojures implementation:

  (mac -> (x . forms)
    (if (single forms)
        (let form (car forms)
          (if (acons form)
              (let (f . args) form
                 (if args 
                     `(,f ,@(cons x args))
                     `(,f ,x)))
              `(,form ,x)))
        (let (form . more) forms
          `(-> (-> ,x ,form) ,@more))))


  (mac ->> (x . forms)
    (if (single forms)
        (let form (car forms)
          (if (acons form)
              (let (f . args) form
                 (if args 
                     `(,f ,@(join args (list x)))
                     `(,f ,x)))
              `(,form ,x)))
        (let (form . more) forms
          `(->> (->> ,x ,form) ,@more))))
In Clojure, however, the thread-first operator works really well when querying deeply nested tables/hash-maps while in Arc these do not. This really is just because of the difference in hash-map/table implementations.

So here is my preferred implementation for Arc:

  (mac -> (x . forms)
    (if (single forms)
      (let form (car forms)
         (if `(isa ,x 'table) 
             `(,x, form)
              (if (acons form)
                 (let (f . args) form
                      (if args 
                          `(,f ,@(join args (list x)))
                          `(,f ,x)))
                 `(,form ,x))))
      (let (form . more) forms
        `(-> (-> ,x ,form) ,@more))))
Which allows for:

  arc> (= animals (obj cat (obj toby (obj age 9))
                       dog (obj thaddeus (obj age 5))))
  ...          
  arc> (-> animals 'dog 'thaddeus 'age)
  5
I believe this will work for your afn/recstring example, but the other one would be a little different:

  arc> (-> [* _ 2] 3)
  6
[note: I had to install arc to give this a try - not having used it for probably a year or so. So if it's really hacky my apologies, but took me 10 mins just to re-learn how to write even really basic expressions, and I certainly don't remember being very good with macros.]

Cheers. Tim


This is interesting, but my first reaction is that push wouldn't necessarily need to be a macro at all with the right reference-passing/modifying semantics. For instance, Arc's scar & scdr manipulate the contents at an address:

  ; Won't work on nil, whatever.  Just a case in point.

  (def my-push (x addr)
    (let old-car (car addr)
      (scar addr x)
      (scdr addr (cons old-car (cdr addr)))
      addr))

  arc> (= xs (= ys '(1 2 3)))
  (1 2 3)
  arc> (my-push 4 ys)
  (4 1 2 3)
  arc> ys
  (4 1 2 3)
  arc> xs
  (4 1 2 3)

  ; versus

  arc> (= xs (= ys '(1 2 3)))
  (1 2 3)
  arc> (push 4 ys)
  (4 1 2 3)
  arc> ys
  (4 1 2 3)
  arc> xs
  (1 2 3)
Coming up with a simultaneously clean, terse, and simple pointer model is a different exercise, I suppose. And not one I'm particularly qualified to participate in (I'm no C guru).

Have you found any other use-cases for this qcase idea?


Still to do: make it varargs along both dimensions (xs and fs).

What do you mean by this? Something like the following?

  arc> (-> 1 2 /)
  2
From reading Clojure's source (https://raw.github.com/clojure/clojure/c6756a8bab137128c8119...), I think the following is an Arc translation of their macro:

  (mac -> (x . rest)
    (if (no rest)
        x
        (let (expr . more) rest
          (if more         `(-> (-> ,x ,expr) ,@more)
              (acons expr) `(,(car expr) ,x ,@(cdr expr))
                           (list expr x)))))
But it seems pretty screwy:

  arc> (macex '(-> 1 2 (fn (x y) (/ x y))))
  (fn (-> 1 2) (x y) (/ x y))
Probably a type system thing with Clojure's idea of seq?.

I like the simplicity of something like this:

  (mac -> exprs
    (reduce (fn (l r) (list r l)) exprs))
but it fails to capture the (-> 1 2 /) use-case.

Maybe this?

  (def -> exprs
    (reduce (fn (l r)
              (let ls (check l alist (list l))
                (if (~isa r 'fn) ; but what about callable objs?
                    (cons r ls)
                    (apply r ls))))
            exprs))

  arc> (def flip (f) (fn (y x) (f x y)))
  #<procedure: flip>
  arc> (-> 1 2 + [fn (x) (+ x _)] '(1 2 3) (flip map))
  (4 5 6)
Though at this point, may as well just model a stack machine in general. Or just use Factor. :)

I would also design a hackable lang. in the very-soon future. Now I have to work and live. hayhay... I like Paul. :)

Just tried your example. Nice!

Just to go through the derivation of his [5 0 1] example:

        *[s [5 0 1]]
  i.e., *[s [5 [0 1]]]  -- because [a b c] == [a [b c]]
  i.e., *[s 5 [0 1]]    -- because [a [b c]] == [a b c]
     => ^*[s [0 1]]     -- by *[a 5 b] => ^*[a b]
  i.e., ^*[s 0 1]       -- because [a [b c]] == [a b c]
     => ^/[1 s]         -- by *[a 0 b] => /[b a]
     => ^s              -- by /[1 a] => a
     => (s + 1)         -- by ^a => (a + 1)
I'm inclined to try his (s - 1) problem, but ain't nobody got time for that.

It's not often the case, but when eval is right, it's right.

Good work! Looks like you have all the angles covered in your point-by-point breakdown at the end, and evaluating is really all the CL version is doing too. Honestly, the biggest "gotcha" for me is point 2, but you neatly deal with that. The tradeoff is totally worth it for having on-line "redefinition" of the macros you're testing. Just make sure 2 is documented as a caveat, and you're good to go.

Certainly a shorter answer to the whole discussion than the naysaying I launched into. ;)


Very interesting! Particularly the <- and -> operators. It's refreshing to see new approaches to stack shuffling that don't come from the kind of established zeitgeist of stack shufflers (dup, swap) & combinators (dip, bi, tri). I was confused by the notions of the "downstack" and "upstack", because it looks to me that you really only have one stack, and a cursor into it, like:

               -- (bottom) n
               --          ^

   <-          -- (bottom) n
               -- ^^^^^^^^

   { '*' << }  -- (bottom) { '*" << } n
               --          ^^^^^^^^^^

   ->          -- (bottom) { '*' << } n
               --                     ^

   times
or

               -- (bottom) a b c
               --              ^

  <-           -- (bottom) a b c
               --            ^

  <-           -- (bottom) a b c
               --          ^

  operate_on_a -- (bottom) whatever operate_on_a produces b c
               --                                ^^^^^^^^

  ->           -- (bottom) whatever operate_on_a produces b c
               --                                         ^
etc.

Also, it seems to me the language isn't really "typeless"; rather, the programs aren't generally type-directed. The system appears similar to Arc's, where you have a few already-used type tags (string, int, num), but in principle the tags are arbitrary. I suppose one distinction could be the treatment of the data under certain operations: Bipedal has syntax for strings and integers, but what happens when you call + on a string and an integer? Is it just treated like adding the string's address with the integer, perhaps?

Finally, I'm interested by the crypto angle. Do you think this is just a generally important (or interesting) feature, or is there a particular application you have in mind? [I said, hoping you're still checking this forum. :)]

2 points by fallintothis 4790 days ago | link | parent | on: Code golf with many Arc solutions

Here I was, trying to put on my code golf hat and beat the submitted solutions. But what do you know? The submitters did excellent jobs.

Cool find!


Thanks so much for the link, that was it.

  $ diff -prauN ac.scm ac.scm.wait 
  --- ac.scm	2013-09-28 10:04:00.907266192 -0400
  +++ ac.scm.wait	2013-09-28 10:03:55.811266793 -0400
  @@ -1035,6 +1035,7 @@
   (xdef new-thread thread)
   (xdef kill-thread kill-thread)
   (xdef break-thread break-thread)
  +(xdef wait-thread thread-wait)
   (xdef current-thread current-thread)
   
   (define (wrapnil f) (lambda args (apply f args) 'nil))
And an example using wait-thread:

  (def th-test() ; how to use wait-thread                                              
    (withs (counter 0 allthreads nil)
           (while (< counter 4)
                  (withs (this nil localcnt counter)
                         (= this (thread (do (prn "thread " localcnt)
                                             (sleep 2))))
                         (push this allthreads))
                  (++ counter))
           (each th allthreads
                 (withs (thisthread nil)
                        (= thisthread (wait-thread th))
                        (prn "thread " thisthread " finished")))))
2 points by akkartik 4790 days ago | link | parent | on: Code golf with many Arc solutions

Thanks. On a related note, I've been having fun populating the Rosetta Code page for wart: http://rosettacode.org/wiki/Category:Wart. After seeing this I just realized that arc has no representation at all!
2 points by zck 4790 days ago | link | parent | on: Code golf with many Arc solutions

Main page here: http://golf.shinh.org/

There are some good solutions: http://golf.shinh.org/p.rb?character+subsets#Arc

and some ones that, I would argue, don't actually fulfill the point of the problem: http://golf.shinh.org/p.rb?irc#Arc


Anything that macroexpands and then executes code is going to be an awful lot like 'eval, so this looks like a good approach to me.

Most promising! I'll need some time to play with this.
2 points by zck 4792 days ago | link | parent | on: Improved plural, pluralize

Oh. Duh.
2 points by akkartik 4792 days ago | link | parent | on: Improved plural, pluralize

"Is there a use case where you're not aware of the exception when calling the function?"

Any situation where the string being pluralized is not a literal. This is I suppose why rails pluralization doesn't work like this.

2 points by thaddeus 4793 days ago | link | parent | on: Improved plural, pluralize

I had imagined that the table would just be defined in advance from a src file. That would be consistent with other comparable features arc offers. And the memoization would just be a code efficiency thing.

If we don't care about being consistent, it certainly would be cool to have the table load from a data file, and then to have the first run of an override write to file when it's not present. Memoization to, again, cover code efficiency.

As for use cases I only had a few plural overrides I needed to deal with and I too just hard coded them. At the time I did so I remember thinking I should put it in a table, but never got around to it. Then I saw your post and I thought of it again!

Note that I don't really use arc these days, but I have ported most arc functions over to clojure and I'm always looking to improve my library. In my clojure world I would just store them in datomic as facts. I think I might run along and do this now that I've spent some time thinking about it ... ;)

2 points by zck 4793 days ago | link | parent | on: Improved plural, pluralize

A lookup table would be interesting. I considered that, but wavered -- would it be filled at write-time, or by some sort of memoization? Keep in mind that if it's memoized, you have to worry about when you tell the function what the plural should be, or you could get inconsistent data.

Is there a use case where you're not aware of the exception when calling the function?

2 points by thaddeus 4793 days ago | link | parent | on: Improved plural, pluralize

I like it.

It seems to me , however, that a better solution would be to store the exceptions in a table, then for plural to check for an exception value before defaulting to adding an s.

As you have defined it, we would need to be aware of each exception when the function gets called, often having to add hard coded data for each exception condition and possibly many times.

[edit: what would be really nice is to keep your code along with using a lookup table + memoization.]

1 point by thaddeus 4793 days ago | link | parent | on: Using Arc with jQuery?

Just out of curiosity, why not just use ClojureScript? It seems like it has already been built for your purposes while arc has not.
1 point by akkartik 4793 days ago | link | parent | on: Nock: An alternative to lambda calculus

See also the discussion on HN: https://news.ycombinator.com/item?id=6438320

Especially why lisp failed: https://news.ycombinator.com/item?id=6441036

Equivalent of arc.arc: https://github.com/urbit/urbit/blob/master/urb/zod/arvo/hoon...


I see racket does provide thread-wait:

http://docs.racket-lang.org/reference/threads.html#%28part._...

Should be easy to add to arc. Can you submit a pull request if you do so?


Thanks.

Shouldn't Arc provide something like pthread_join? A function that wakes up the thread that waits -- rather than continually checking for all threads to exit?


Thanks! So weird that I missed that, especially the first.

Slick! But just to play human lint tool:

  (~all t (map dead threads))
is the same as

  (~all dead threads)
and

  (while (~all dead threads)
    (sleep 1))
is the same as

  (until (all dead threads)
    (sleep 1))
which may read slightly better.

http://arclanguage.github.io/ref/threading.html

You could sleep and periodically wake up to check if all the threads are dead.

  (def wait-for threads
    (while (~all t (map dead threads))
      (sleep 1)))

  arc> (do (wait-for (thread:do sleep.1 prn.1)
                     (thread:do sleep.2 prn.2)
                     (thread:do sleep.3 prn.3))
           (prn "Done!"))
  1
  2
  3
  Done!
  arc>
More