Arc Forumnew | comments | leaders | submitlogin

Thank you for detailed answer, now reasons are more clear to me!
4 points by rocketnia 4646 days ago | link | parent | on: Is there a repository of Arc libraries?

Historically speaking, Arc was put forward as a language for experimentation. If you want to package up code in a squeaky clean format for others to use who don't care about implementation details, you're doing something other than experimentation; you're publishing a product people would supposedly rely on.

Moreover. not even the Arc language is stable (or so this website has always said). If it encouraged people to package up their stable code, that would prove deceptive once the whole foundation fell away. This happened to Anarki when Arc 3 was released; the community had built up lots of Arc 2 code, and not much of that was ported to work on Arc 3.

However, Arc is not without module systems. The first thing I did in Arc was build some namespace macros with explicit imports and exports for my own benefit (Lathe). Andrew Wilcox made "the hackinator" which I think could download Arc programs from a Web-hosted manifest file, most often from GitHub.

More recently, Arc/Nu is an Arc implementation with rich support for first-class namespaces, along with several utilities for modular loading of Arc code that's written in the usual sequence-of-commands style. Arc/Nu is probably the best platform to publish stable modules for, since it can load alternate namespaces to achieve backwards compatibility.


Why there isn't any module system yet?
2 points by akkartik 4646 days ago | link | parent | on: How to read mutable hash tables

Yes, let is just a simpler form of withs, but it's worth using because it signals that you only need one var and therefore skips extra parens.

withs is the sequential form of with, where you'd like each variable available in defining later variables within the with. You have to say:

  arc> (withs (x 1 y (* x 2)) (+ x y))
..but you can use the simpler with in:

  arc> (with (x 1 y 2) (+ x y))
Not only can you, you should. Using the most general form when a simpler form will do is akin to crying wolf; it's very useful when reading code to be able to tell simpler parts from more complex parts at a single glance.

So use with to signal that you're defining multiple variables at once. Use withs to signal that there's a dependency between the definitions. If there's no dependency, with is more idiomatic. And if there's only one variable, let is more idiomatic. Finally, a withs with multiple bodies and a trivial body is easier to read if you put the final expressions into the body. This:

  (withs (x 1
          y (f x z))
    y)
is better written as:

  (withs (x 1)
    (f x z))
and therefore:

  (let x 1
    (f x z))
(and further to (f 1 z), but let's pretend x is being bound to something less trivial.)

Even if a body is a little more complex:

  (withs (x complex-expr1
          y (complex-expr2 x)
          z (complex-expr3 y))
    (car z))
it's sometimes nicer to read as:

  (withs (x complex-expr1
          y (complex-expr2 x))
    (car (complex-expr3 y)))
let, with and withs are nothing but macros, so if you avoid using them you're giving up the benefits of lisp. Might as well go back to a weaker language. In general, my idea when learning lisp was: "Since I come from an imperative background my tendency is to define intermediate variables. Therefore I will start by avoiding all temporaries, and only after I get it working will I insert the fewest possible temporaries to make the code readable." It's stood me in good stead.
3 points by thaddeus 4646 days ago | link | parent | on: Is there a repository of Arc libraries?

There's a library directory for Anarki which seems to be a common place for the user submitted libraries.

https://github.com/arclanguage/anarki/tree/master/lib

2 points by lark 4646 days ago | link | parent | on: How to read mutable hash tables

Thank you for the feedback. Thanks also to fallintothis: I wish the copy functions you wrote omitted unnecessary lines of code.

a,b) Thanks, fixed:

  (def make-mutable-cons (data)
    (withs (ret nil)
           (each el data
                 (if (is (type el) 'table)
                     (push (make-mutable-table el) ret)
                     (is (type el) 'cons)
                     (push (make-mutable-cons el) ret)
                     (push el ret)))
           (rev ret)))
c) let doesn't work with more than one variable; so withs is more general (should let even exist?). I can't tell what the difference between with and withs is, and I had picked the latter when I started needing more than one variable. What is their difference?

d) Seems sym's not needed; removed.

2 points by zck 4646 days ago | link | parent | on: Is there a repository of Arc libraries?

There isn't. I was considering writing something like quicklisp for Arc, but the downside is that with no module system, recursive loading of dependencies will break two different pieces of code that depend on different versions of a library.

We should probably at least make a wiki page with libraries, though.

1 point by akkartik 4647 days ago | link | parent | on: How to getpid in Arc 3.1

Thanks!
2 points by fallintothis 4648 days ago | link | parent | on: How to read mutable hash tables

Disk serialization of variables & tables is already part of Arc 3.1's fromdisk, diskvar, disktable, and todisk:

  $ arc
  Use (quit) to quit, (tl) to return here after an interrupt.
  arc> (disktable h "/tmp/nested")
  #hash()
  arc> (= h (obj this "this" that 3 nested-hash-table (obj one "one" two "two" three 3) some-list (list 1 2 "three" 4)))
  #hash((that . 3) (some-list . (1 2 "three" 4 . nil)) (this . "this") (nested-hash-table . #hash((one . "one") (two . "two") (three . 3))))
  arc> (todisk h)
  ((nested-hash-table #hash((one . "one") (two . "two") (three . 3))) (this "this") (some-list (1 2 "three" 4)) (that 3))
  arc> (quit)
  $ cat /tmp/nested && echo
  ((nested-hash-table #hash((one . "one") (two . "two") (three . 3))) (this "this") (some-list (1 2 "three" 4)) (that 3))
  $ arc
  Use (quit) to quit, (tl) to return here after an interrupt.
  arc> (disktable nt "/tmp/nested")
  #hash((that . 3) (some-list . (1 2 "three" 4)) (this . "this") (nested-hash-table . #hash((one . "one") (two . "two") (three . 3))))
But, as I'm sure you're aware, the error with the above is that the serialized table (returned by todisk) has a literal Scheme #hash object nested inside, and when you read that back in, it's immutable:

  arc> (= (nt "this") "THIS")
  "THIS"
  arc> (nt "this")
  "THIS"
  arc> (= (nt 'some-list) (list 5 6 "seven"))
  (5 6 "seven")
  arc> (nt 'some-list)
  (5 6 "seven")
  arc> nt
  #hash((that . 3) (some-list . (5 6 "seven" . nil)) (this . "this") (nested-hash-table . #hash((one . "one") (two . "two") (three . 3))) ("this" . "THIS"))
  arc> (= ((nt 'nested-hash-table) 'one) "ONE")
  Error: "hash-set!: contract violation\n  expected: (and/c hash? (not/c immutable?))\n  given: #hash((one . \"one\") (two . \"two\") (three . 3))\n  argument position: 1st\n  other arguments...:\n   one\n   \"ONE\""
So, the actual salient operation here has little to do with immutable tables, but rather "deep copying" them, an operator that vanilla Arc lacks. It only has copy:

  (def copy (x . args)
    (let x2 (case (type x)
              sym    x
              cons   (copylist x) ; (apply (fn args args) x)
              string (let new (newstring (len x))
                       (forlen i x
                         (= (new i) (x i)))
                       new)
              table  (let new (table)
                       (each (k v) x 
                         (= (new k) v))
                       new)
                     (err "Can't copy " x))
      (map (fn ((k v)) (= (x2 k) v))
           (pair args))
      x2))
But we could model a deep-copy off of that:

  ; (map deep-copy xs) wouldn't work on dotted lists, hence this helper
  (def deep-copylist (xs)
    (if (no xs)
         nil
        (atom xs)
         (deep-copy xs)
         (cons (deep-copy (car xs))
               (deep-copylist (cdr xs)))))

  (def deep-copy (x . args)
    (let x2 (case (type x)
              sym    x
              char   x
              int    x
              num    x
              cons   (deep-copylist x)
              string (copy x)
              table  (let new (table)
                       (each (k v) x
                         (= (new (deep-copy k)) (deep-copy v)))
                       new)
                     (err "Can't deep copy " x))
      (map (fn ((k v)) (= (x2 (deep-copy k)) (deep-copy v)))
           (pair args))
      x2))
Then we have

  arc> (disktable nt "/tmp/nested")
  #hash((some-list . (1 2 "three" 4)) (this . "this") (nested-hash-table . #hash((one . "one") (two . "two") (three . 3))) (that . 3))
  arc> (= nt (deep-copy nt))
  #hash((nested-hash-table . #hash((three . 3) (one . "one") (two . "two"))) (this . "this") (that . 3) (some-list . (1 2 "three" 4 . nil)))
  arc> (= (nt "this") "THIS")
  "THIS"
  arc> (= ((nt 'nested-hash-table) 'one) "ONE")
  "ONE"
  arc> (= (nt 'some-list) (list 5 6 "seven"))
  (5 6 "seven")
  arc> nt
  #hash((nested-hash-table . #hash((three . 3) (one . "ONE") (two . "two"))) (this . "this") (that . 3) ("this" . "THIS") (some-list . (5 6 "seven" . nil)))
You could build a macro atop of fromdisk to handle deep copying automatically.

I seem to remember discussion about deep copying before, but all I could find was http://arclanguage.org/item?id=16979.

4 points by akkartik 4648 days ago | link | parent | on: How to read mutable hash tables

Cool. Some comments:

a) You're reversing your lists, like the value of some-list above.

b) If you include a table inside a list, I think riff-table won't make it mutable. Here's a slightly different file

  arc> (= h (w/infile f "nested" (read f)))
  #hash((this . "this") (nested-hash-table . #hash((three . 3) (one . "one") (two . "two"))) (some-list . (1 2 "three" #hash((four . "foo")) . nil)) (that . 3))
  arc> h!some-list
  (1 2 "three" #hash((four . "foo")))
  arc> h!some-list.3
  #hash((four . "foo"))
  arc> h!some-list.3!four
  "foo"
  arc> (= h!some-list.3!four "bar")
  Error: "hash-set!: contract violation\n  expected: (and/c hash? (not/c immutable?))\n  given: '#hash((four . \"foo\"))\n  argument position: 1st\n  other arguments...:\n   'four\n   \"bar\""
You might want to see how anarki fixes the read and write primitives to be fully general for tables as well as user-defined types:

  arc> (tofile "x" (write:obj a 1 b 2 c (list 2 3 (obj d 4 e 5 f 6))))
  nil
  arc> (= h (fromfile "x" (read)))
  #hash((a . 1) (c . (2 3 #hash((f . 6) (e . 5) (d . 4)))) (b . 2))
  arc> (= h!a 34)
  34
  arc> (= h!c.2!g 7)
  7
  arc> h
  #hash((a . 34) (c . (2 3 #hash((g . 7) (f . 6) (e . 5) (d . 4)))) (b . 2))
c) Why you no like let and with? :) I'd rewrite your expressions above as:

  (def make-mutable-cons (data)
    (let ret nil
      (each el data
        (push el ret))
      ret))

  (def make-mutable-table (data)
    (w/table ret  ; w/table implicitly returns ret
      (maptable (fn (k v)
		  (if (is (type v) 'table)
		       (= (ret (sym k)) (make-mutable-table v))
		      (is (type v) 'cons)
		       (= (ret (sym k)) (make-mutable-cons v))
		      (= (ret (sym k)) v)))
		data)))

  (def riff-table (file)
    (make-mutable-table
      (w/infile i file
	(read i))))
d) Why do you have those calls to sym?
1 point by akkartik 4658 days ago | link | parent | on: Julia

I finally installed Julia, and my comment needs a correction. Macros sometimes don't need the parens.

  julia> macro foo(ex)
	   :($ex ? nothing : error("Assertion failed: ", $(string(ex))))
	 end

  julia> @foo 2 > 0

  julia> @foo 2 > 3
  ERROR: Assertion failed: :((2>3))
   in error at error.jl:22
However, I have not been able to get this to work with multiple args:

  julia> macro foo(ex, msg)
	   :($ex ? nothing : error("$($msg): ", $(string(ex))))
	 end

  julia> @foo(2 > 3, "aaa")
  ERROR: aaa: :((2>3))
   in error at error.jl:22

  julia> @foo 2 > 3, "aaa"
  ERROR: wrong number of arguments
I don't know if this is a real limitation, or I just don't know enough yet.
2 points by akkartik 4658 days ago | link | parent | on: Julia

You'll like the old discussion on Bullet which had some similar ideas, including considering how to suppress paren-insertion inside such a special operator, etc.

http://web.archive.org/web/20120210015823/http://seertaak.po... (http://arclanguage.org/item?id=15769; http://www.reddit.com/r/programming/comments/pekx5/bullet_ad...)

2 points by malisper 4658 days ago | link | parent | on: Julia

I realized that we actually don't need to have a closing bracket to signal the end of the expression. We just need to implement something like the $ operator in haskell.

  def (foo a b c)
    if $ and
           a = 5
           b = 10
           c = 15
      prn "success"
      prn "failed"
There should be enough information based off of the indentation to tell how it should be parsed. $ should mean something along the lines of everything indented after the next symbol is a single expression.
1 point by akkartik 4659 days ago | link | parent | on: Julia

Ok, interesting idea that I hadn't considered before.

As it happens, I've been watching a discussion about a different use for brackets: http://lambda-the-ultimate.org/node/4879. Lots of people (scheme, clojure, this liso guy) seem to have an idea about what to use brackets for, and it's not clear what the best use is for these precious punctuation characters.

2 points by malisper 4659 days ago | link | parent | on: Julia

Yes, parens will not use indentation sensitivity, but brackets will. In that case the examples you gave would be valid code.
1 point by akkartik 4659 days ago | link | parent | on: Julia

But you have to have some way to suppress indent-sensitivity. How would you represent these?

  '(a reeeeeaaaally
    long line)

  def (foo a b c
           d e f)
    ..
Oh, are you suggesting suppressing indentation inside () but not inside []?
2 points by malisper 4659 days ago | link | parent | on: Julia

Just have brackets (maybe curly braces) use indentation sensitivty.

  def (foo x y z)
    if [and
          x = 5
          y = 10
          z = 15]
      prn "success"
      prn "failed"
instead of

  def (foo x y z)
    if (and (x = 5) (y = 10) (z = 15))
      prn "success"
      prn "failed"
or

  def (foo x y z)
    if (and (x = 5)
            (y = 10)
            (z = 10))
      prn "success"
      prn "failed"
2 points by akkartik 4659 days ago | link | parent | on: Julia

Another issue with just disabling transforms inside quotes. This would stop working:

  ',car.x
1 point by akkartik 4660 days ago | link | parent | on: Julia

Thanks for the investigation! Yes, that would be the way to disable transforms inside a quoted s-expr.

But I think that still would leave issues. For one, it is approximately as likely that a list innocuously contains a literal '+ as that we're constructing a fragment of code that is eventually intended to be eval'd. We need a way to say, "this is code" or "this is never going to be eval'd." A second issue is that quoting isn't the only way to read data. Imagine using read to read a list from a file. How would we suppress infix there?

I actually think reading data from a file is the bigger issue. Small quoted lists in code will be noticed, and can be replaced with some (klunky) combination of list and cons. It's far worse if you have a multi-megabyte file that silently gets corrupted because of one character.

2 points by akkartik 4660 days ago | link | parent | on: Julia

I'm happy to rethink it if you can suggest an alternative way to suppress indent-sensitivity. I didn't have the second rule at the start, but was swayed by a discussion with the "readable s-expressions" project: https://www.mail-archive.com/readable-discuss@lists.sourcefo...; https://www.mail-archive.com/readable-discuss@lists.sourcefo.... It makes for an easily understood rule. What I had before took more than two sentences to explain: https://github.com/akkartik/wart/tree/7db61146b0#readme (search for 'suppress grouping')
2 points by malisper 4660 days ago | link | parent | on: Julia

I'm just wondering if the second rule is really such a good idea. If one wants to use indentation-sensitivity within parens, it is impossible. I'm not sure how often trying to write code like that would come up in practice, but I think the programmer should have the option in that case of whether indentation-sensitivity is actually being used (by using brackets or something else to make it clear).
2 points by malisper 4660 days ago | link | parent | on: Julia

I'm going to have to familiarize myself with the internals before I actually try to fix it. After looking for a little bit, I think the issue is in the transform_infix function where it handles the quoting.

  if (is_quote_or_unquote(n.elems.front())) {
    list<ast_node>::iterator p = n.elems.begin();
    while (is_quote_or_unquote(*p)) {
      trace("infix") << "skipping past " << *p;
      ++p;
      if (p == n.elems.end()) {
        trace("infix") << "just quotes/unquotes " << n;
        return n;
      }
    }
I would think that after you determine that the first element is a quote, you would just ignore the rest instead of going through and checking which ones are also quoted. Since I'm not that familiar with c++ or how the internals of wart work, I'm going to have to leave it to you to see if this is actually the problem.
1 point by akkartik 4660 days ago | link | parent | on: Julia

Nice!

Yeah, that's a bug. I haven't yet bothered to create a version of read without the infix transform. For now you have to:

  '(5 (+) 5)
If you decide to try to fix this I'd love to hear your experiences. The point of wart was to be easy to hack on, but I'm losing steam because it's been hard to get feedback on that score.
3 points by malisper 4660 days ago | link | parent | on: Julia

I kept looking and found that read has the functionality I was looking for.

  (read)
  5+5

  => (+ 5 5)
I also noticed that if I want to generate lists which are expressions, it is not as straightforward as it should be (I'm trying to get a list of the number 5 the symbol + and 5 again but wart for whatever reason rearranges them).

  '(5 + 5) 
   => (+ 5 5)
I'm going to guess this is a bug.
2 points by akkartik 4660 days ago | link | parent | on: Julia

Thanks for trying it out! I've tried very hard to keep the rules minimal and easy to understand. Only two sentences in https://github.com/akkartik/wart/blob/c73dcd8d6/004optional_... are rules:

1. "Multi-word lines without leading parens are implicitly grouped with later indented lines."

2. "Indentation-sensitivity is disabled inside parens."

That's really all there is to it.

> is there any way to see what my code looks like if I were to type it using parenthesis?

It's a little clunkier than visualizing infix, but you can introspect on any function or macro. Here's an example session:

  $ ./wart
  ready! type in an expression, then hit enter twice. ctrl-d exits.

  def (foo x)
    if a b
      c d
      :else e

  foo
  => (object function {sig, body, })  # wart doesn't print table values by default

  (body foo)
  => ((if a b (c d) (:else e)))
Here you can see that turning (c d) and (:else e) into calls is probably not desired.
2 points by malisper 4660 days ago | link | parent | on: Julia

I just quickly tried wart and it does seem pretty impressive to me. The only thing I'm worried about is the rules for how something is grouped. I may just need to use it more to see if it's an actual issue, but I feel that having all of the different rules can make it very easy to write hard to find bugs. Other than that I find wart to be a very interesting idea which will be worth checking out.

Also is there any way to see what my code looks like if I were to type it using parenthesis?

3 points by akkartik 4661 days ago | link | parent | on: Julia

I haven't actually played with it, but from reading the docs[1] Julia's macros -- like Dylan's or Nemerle's -- feel clunky compared to orthodox lisp. The clunkiness isn't caused by infix, though. Infix is easy to desugar, which might be easier to see with wart:

  $ git clone http://github.com/akkartik/wart
  $ git checkout c73dcd8d6  # wart is currently (and perhaps indefinitely) broken
  $ ./wart
  'a+b
  => (+ a b)
No, the real trouble lies in their use of f(x) syntax for function calls rather than (f x). As a result there's no way for a when macro to look like the builtin if keyword.[2]

That said, I'm sure it's useful to have even some clunky way to do code-generation. The key is that they stuck with a purely expression-based language, and didn't give in to the siren song of statements.

[1] http://docs.julialang.org/en/latest/manual/metaprogramming

[2] http://arclanguage.org/item?id=16924

2 points by zck 4661 days ago | link | parent | on: Julia

I saw a presentation by Stefan Karpinski, one of the Julia creators. It's a very interesting language that I'm sad to report that I have absolutely no reason to use. Unfortunately, I don't do any kind of mathematical computing.

I'm not too sad, because I do prefer prefix languages, but if I had to do something in that realm, I would definitely try out Julia.


You got me, that's where I stole the idea from.

There was some interesting discussion on that in the Reddit thread. :)
More