Arc Forumnew | comments | leaders | submitlogin
1 point by akkartik 4006 days ago | link | parent

Ok, I've updated http://github.com/akkartik/arc. It was a useful exercise, because it turns out it has more tests than anarki, and I found several bugs in my work yesterday :) I've copied the tests to anarki as well.


1 point by akkartik 4005 days ago | link

I tried to build a new optional keyword arg for each to mimic ontree, but ran into some problems. Consider the definition of each:

  (mac each (var expr . body)
    `(walk ,expr (fn (,var) ,@body)))
It doesn't really need to know about a new arg, but there's no way to pass it in automatically because there's already a rest arg: body. Ok, so let's try that:

  (mac each (var expr ? as nil . body)
    `(walk ,expr :as as (fn (,var) ,@body)))
The problem with this is that you need to explicitly provide the optional arg, or it gets taken out of body.

  arc> (prn:macex1:quote (each x '(1 2 3) x))
  (walk '(1 2 3) :as x (fn (x)))               ; wrong

  arc> (prn:macex1:quote (each x '(1 2 3) :body x))
  (walk '(1 2 3) :as () (fn (x) x))
I had this old idea that rest params should take precedence over optionals, but it turns out to have fallen by the wayside: http://www.arclanguage.org/item?id=13083. Time to bring it back here.

-----

2 points by malisper 4005 days ago | link

Well, the problem is you are trying to make keyword args the same as optional args. It might be better if you make make keyword args and optional args two completely separate kinds of arguments (similar to how common lisp has both &optional and &key). This way you would define each as:

  (def each (var expr : as nil . body) ; I used : to represent keyword args
    `(walk ,expr :as as (fn (,var) ,@body)))
This way calling each like

  (each x '(1 2 3) x)
will expand correctly because keywords only take a value when they are passed in explicitly. It would have to be called as

  (each x '(1 2 3) :as x)
to get the error you had. If you need to define a function that takes both optional args and keyword args:

  (def foo (a b c ? g h i : j k l . rest)
     ...)
This way a-c are required, g-i are optional, j-l are keyword, and rest is everything else.

As for the problem you had with needing to redefine each (it is also mentioned by rocketnia in the post you linked[1]), each is just an interface for walk, so if a new feature is added to walk, each need to be redesigned. There might be some way that we can define some sort of macro, interface, which can be used to create a function/macro which is a very thin layer over another function/macro. As for how interface is to be designed, I'm not sure.

[1] http://www.arclanguage.org/item?id=13090

-----

1 point by akkartik 4005 days ago | link

Well, it seems more general to let any arg be acceptable as a keyword.

rocketnia's concern I addressed by passing through unknown keyword args unchanged:

  (def foo (a b)
    (list a b))

  (def bar args
    (apply foo args))

  arc> (bar 3 :a 4)
  (4 3)
But that doesn't work in this case because each needs to treat as specially. I don't think that's avoidable.

We _could_ include a new category of 'required' keyword args like you suggest, yes. Let me know if you think of a situation where just giving rest args precedence isn't enough. Things are already pretty complicated with rest, destructured, optional, and keyword args, so I'm reluctant to add another delimiter and category.

-----

2 points by rocketnia 4003 days ago | link

"rocketnia's concern I addressed by passing through unknown keyword args unchanged"

The reason I'm okay with this approach is because it localizes the complexity to each function. This way I don't dislike the language; I just dislike the idioms currently being used in it.

Consider the use of flat text formats for shell scripts, HTTP headers, CSS properties, XML attributes, etc. They're usually convenient enough to type out manually, and that also makes them convenient to generate in simple cases, but writing a parser for them can be tough due to the same syntactic sugars that made them convenient to generate in the first place. Writing a full-featured generator or parser can also be tough, because sometimes future versions of the text format may allow it to encode more information.

With keyword arguments, the syntax for a function call is more complicated and sugary, putting programmers like me in the position of wondering what kind of headaches they should go through to parse or generate this format. If the headaches are unclear, combining programs becomes hard, because each developer might opt for different headaches, at which point I think they overlap in the form of complex glue code. :)

-----

2 points by akkartik 4003 days ago | link

I have a greatly-enhanced respect for this perspective over the past month. Look at my travails over the past day trying to get optional, keyword and destructured args to play well together: https://github.com/akkartik/arc/compare/342ebf57a8...e347973.... Or my travails over the past month trying to build macex in wart in the presence of optional, keyword and destructured args AND quoted AND aliased params AND already-eval'd args[1] AND incompletely-eval'd args[2]: https://github.com/akkartik/wart/compare/30025ee25c...cabaa0.... Clearly there's some point at which I jump the shark :)

[1] related to apply for macros: http://arclanguage.org/item?id=16378

[2] related to partial eval: https://github.com/akkartik/wart/commit/8239ed9d21. I've been struggling intermittently with partial eval for two years now. This now year-long attempt might well be utterly useless.

---

Perhaps the easiest to understand and most serious extant example of the knots I end up tying myself into:

  arc> (iso :x :x)
  nil
This is because x is one of the params of iso. Even more serious, this happens even if you try to compare a variable that evaluates to :x. Or a variable that evaluates to a large structure containing :x somewhere in it. Ugh! The reason for this is that I'm extracting keyword args at function-call time inside each function. This:

  (def foo (x) x)
compiles down to something like:

  (define (foo allargs)
    (let ((keyword-args  (extract-keywords allargs '(x)))
      ..))
It took me three years to notice yesterday that I'm extracting keywords from post-evaluated arguments. Ugh! Urrrgggh!

Weird thing is, even wart has this problem. Even though I would superficially seem to be extracting keyword args pre-evaluation. I still need to debug this. Or perhaps I shouldn't debug this. Perhaps I should just throw up my hands and delete all my repos and become a hermit.

-----

1 point by akkartik 4003 days ago | link

Ok, my repo now supports both formats:

  (each x '(1 2 (3 4)) :like 'code prn.x)

  (each x (code '(1 2 (3 4))) prn.x)
We'll see over time if one of the options is superior.

-----