Arc Forumnew | comments | leaders | submitlogin

Yep, that's what I did. Thanks for the advice akkartik -- that seems like a simpler pattern to follow.

I'm still finding that line between functions and macros :)


The issue is with automaton; you're trying to use a macro (mkrule) like a function with map1. Did you turn mkrule into [mkrule _] because you got this error?

  Can't coerce  #(tagged mac #<procedure: mkrule>) fn
Simply using square brackets isn't going to fix that.

---

In general, creating macro helpers for macros is harder to debug. Instead create function helpers to create the right code as a list and invoke it within the top-level macro to turn it into code. Here mkrule should probably be a function.

(This is what people usually mean when they say *don't overuse macros". And then the dictum gradually gets over-generalized into "macros bad" and finally, "lisp? blub blub.")


In the definition of 'automaton, you have the macro call (mkrule _). I don't think you want 'mkrule to be a macro, because I don't think you actually care about the symbol _. The first thing I suggest is to replace "mac mkrule" with "def mkrule".

After this change, I'm still not quite sure what your code is trying to do (I haven't clicked your links yet), so I'll try some example cases:

  arc> (mktransition '(1 2 3 4 5))
  (1 (5 (cdr str)))
  
  arc> (mktransitions '((2 3) (4 5)))
  (
    2 (3 (cdr str))
    4 (5 (cdr str)))
  
  arc> (mkrule '(1 (2 3) (4 5)))
  (list (car (1 (2 3) (4 5)))
        (fn (str)
          (if (empty str)
            t
            (case (car str)
              2 (3 (cdr str))
              4 (5 (cdr str))))))
For one thing, it looks like you might want that (car (1 (2 3) (4 5))) to be just 1. You can fix this by changing (car ,i) to ,(car i).

Since you end up with function calls like (3 (cdr str)), it looks like you're trying to make several functions under the same scope (using Anarki's 'withr/p), and it looks like 'str is a variable that contains the remaining inputs of the automaton.

In this case, I recommend changing `(list (cdr ,i) (fn ...)) so that it's (list (cdr i) `(fn ...)), since you probably didn't want that 'list symbol in the 'withr/p call.

To make this easy to test, here's the complete code, with Anarki's definition of 'withr/p and my updates to 'mkrule:

  ; a 'with that works for defining recursive fns
  (mac withr/p (bindings . body)
    " Scheme's 'letrec.
      See also [[withr]] [[where]] "
    `(let ,(map1 car bindings) nil
       ,@(map [cons 'assign _] bindings)
       ,@body))
  
  (def mktransition (tn) (list (car tn) (list (last tn) '(cdr str))))
  
  (def mktransitions (ts)
    (accum accfn
      (each x (map1 mktransition ts)
        (accfn (car x))
        (accfn (last x)))))
  
  (def mkrule (r)
    (let i r
      (list (car i) `(fn (str)
                        (if (empty str) t
                          (case (car str)
                            ,@(mktransitions (cdr i))))))))
  
  (mac automaton (i r) `(withr/p ,(map1 [mkrule _] r) ,i))
Seems to work!

  (= matches-aabb
    (automaton step-a
      (
        (step-a
          (a step-a)
          (b step-b))
        (step-b
          (b step-b)))))
  
  arc> (matches-aabb '(a a a a b b b b))
  t
  arc> (matches-aabb '(a a a a b b b b a))
  nil
  arc> (matches-aabb '(a a a a))
  t
  arc> (matches-aabb '(b b))
  t
  arc> (matches-aabb '())
  t
  arc> (matches-aabb '(b a))
  nil
  arc> (matches-aabb '(c))
  nil
Now that I look at your Gist, I realize I could have saved myself some time figuring this out. :) I also realize you were aiming for a slightly different interface:

  ; example call:
  ; (automaton 'init
  ;            '((init (c more))
  ;              (more (a more)
  ;                    (d more)
  ;                    (r end))
  ;              (end)))
If you want this interface, you can change 'automaton so it's a function that calls 'eval, rather than a macro:

  -(mac automaton (i r) `(withr/p ,(map1 [mkrule _] r) ,i))
  +(def automaton (i r) (eval `(withr/p ,(map1 [mkrule _] r) ,i)))

Posted a gist with comments: https://gist.github.com/brianru/6133553
2 points by kogir 4850 days ago | link | parent | on: Rark - An Arc inspired Racket language.

It's currently missing extensible setforms, but all the standard ssyntax and brackets should work. They're detected and expanded (awkwardly right now) at the reader level.
3 points by kogir 4850 days ago | link | parent | on: Rark - An Arc inspired Racket language.

It's hard to say since Arc doesn't have a formal spec. The end goal is for everything to just work like it would in pg's Arc. I'm slowly working toward that goal.

Right now, Arc s-exp editing macros are straight-up unsupported. In addition to pattern matching, you have to explicitly break hygiene with leak and bind:

  (mac nif (test body ...)
    #:leak (nit)
    (bind (nit test)
      (if nit body ...)))
  
  > (nif 5 (displayln (+ "yep: " nit)) (displayln (+ "nope: " nit)))
  yep: 5
  
  > (nif nil (displayln (+ "yep: " nit)) (displayln (+ "nope: " nit)))
  nope: 
There's more I'm sure.
2 points by rocketnia 4850 days ago | link | parent | on: Rark - An Arc inspired Racket language.

Good error reporting is pretty disagreeable to me, lol. :-p

I think Arc's main advantages over Scheme, once all the Arc-like naming conventions and macros are in place, are setforms, defcall (not in pg's Arc), and the ssyntaxes (a:b c), a.b, and a!b. Going by the examples, Rark has setforms, and its ability to unwrap data structures using function calls indicates defcall wouldn't be hard to add if it isn't there already, but I don't see anything about ssyntax. I've been meaning to download and run Rark to see if ssyntax support is actually there after all.

The abstract "Scheme" I'm talking about might have a certain advantage over Racket, but I might just be doing it wrong: Is possible to write a macro and use it in the same file? I've had to break my utilities into three files just to have multiple layers of macros. I'm interested in seeing whether Rark makes this any easier.

1 point by akkartik 4850 days ago | link | parent | on: Rark - An Arc inspired Racket language.

Can you point out the most disagreeable changes? :)
2 points by zck 4851 days ago | link | parent | on: Why do old/odd languages appeal to me?

I wonder if it's treated as a second-class citizen, or if you can easily switch between the two.
1 point by akkartik 4851 days ago | link | parent | on: Why do old/odd languages appeal to me?

BTW, a friend mentioned that APL does indeed have an 'english' mode as a debugging aid. Perhaps it's indicative that people are using the more concise notation in spite of the availability of alternatives.
2 points by akkartik 4851 days ago | link | parent | on: Why do old/odd languages appeal to me?

"This would allow robomartin to have his "notation as a tool for thought", while still having another syntax that's easier to understand, for those that want it."

Relevant here is also http://worrydream.com/LearnableProgramming, and the Boxer paper (http://www.soe.berkeley.edu/boxer/20reasons.pdf; I could swear I'd submitted it here before.)

For the past 6 months I've been going to a hackerspace in Oakland every monday evening, and trying to learn how to teach programming to non-programmers. So I've been thinking about this a lot.

2 points by zck 4851 days ago | link | parent | on: Why do old/odd languages appeal to me?

> Take a page full of intense mathematical notation or an orchestral score and translate it into either full words (i.e.: "integral" in place of the integral symbol) or one, two or three character ASCII mnemonics. You should realize that the original is far more powerful a tool for communicating thoughts, ideas and the subject at hand than a seemingly random dump of ASCII on paper.

This comment (https://news.ycombinator.com/item?id=6119221) fascinates me; previously, I've had the same reaction to APL previously.

I've heard this idea before: have a language that separates the chrome of the top-level syntax from the underlying code.^1 That is, you can have the same language use different syntaxes ^2. This would allow robomartin to have his "notation as a tool for thought", while still having another syntax that's easier to understand, for those that want it.

But, of course, this would be difficult, especially if you want to allow mixing of multiple syntaxes in the same line of code.

For this specific example, you could have aliases for APL tokens to (possibly) multiple J tokens. The reader of the code could decide how to view it.^3 The writer could write whatever he or she prefers. But this doesn't even seem to be enough here; I don't think J and APL have all the same operators; unless you can make APL operators easily by combining J ones, this won't work. Of course, that doesn't mean a newly-designed language won't work.

Also, it's hard enough to get one language adopted by people; trying to get a language with variable syntax must be significantly harder. The toolchain will be an extremely important part of language evangelism; if there's no good way for people who don't use, say, either Emacs or Vim to use this language, no one will.

Perhaps start with an even simpler J version; instead of

  avg=: +/ % #
you would write ^4:

  avg=: fold(+) divide list-len
Why do I think people would tolerate having multiple syntaxes for the same thing? Let's take a musical interlude here. Now, music is traditionally written on as sheet music: on a staff, with different notes to indicate length, and the vertical position on the staff to indicate pitch. However, for guitars, there's a more common way of writing it: guitar tab. This throws away information like timing, and simply says "fret X on string Y".^5 Like our J-analogue for APL, tab is much simpler; if you can play the guitar, you can understand tab in a few minutes. Staff takes much longer; weeks or months. People don't go back and forth between one or the other; generally, if you're comfortable working with sheet music, you use that if you have the choice, only choosing tab if you don't have sheet music. I don't know if someone's designed a tool to translate between sheet music and tab.

[1] Heck, even Lisp started that way, with M-expressions: https://en.wikipedia.org/wiki/M-expression . That no one used them is possibly an indication this is a bad idea.

[2] It's a fool's argument, but not technically wrong, to say we have that with different languages that compile to assembly, or to C, or scheme, or whatever. That's missing the point.

[3] This sounds like a nice Emacs mode.

[4] Yes; I'm suggesting a "simple" version include the terminology "fold". It's an implementation detail; perhaps this is better:

  avg=: +-list divide list-len
[5] Note that guitar tab specifically says which string to use: since a single note can be played multiple ways on a guitar (e.g., the open A string is enharmonically the same to the fifth fret on the low E), guitar tab has information which sheet music does not. Whether this is a benefit or not is hotly debated.
2 points by kogir 4856 days ago | link | parent | on: Rark - An Arc inspired Racket language.

This is my fun little side project - the aim being to get Arc running inside of Racket with a little less friction: better performance, profiling support and source locations for errors.

It can run some Arc code unmodified. You'll likely be more comfortable with rark/load (which defines everything at the top level, and is hopeless [1]).

  [1.1]: https://www.google.com/search?q=racket+the+top+level+is+hopeless
  [1.2]: https://gist.github.com/samth/3083053

I shared this over a year ago (http://www.arclanguage.org/item?id=16117); seems to have switched a few gears since.

Well, we'll give you more constructive criticism. Often of the form, "see here for how I did it" :p

Exceptionally. I'll put it back up with a better ReadMe once I've verified I didn't accidentally leak anything I shouldn't have.

Let's just say it was bad enough that someone emailed PG to express despair at the direction things seemed to be heading.


It's nice to see this spelled out with examples. :)

"If you looked at it closely, I made many changes in the spirit of getting something running that PG (and you!) would likely not agree with."

I didn't see the repo while it was online, but I've always looked forward to Arc (or at least Anarki) becoming usable as a Racket language at some point, even if it were an awkward fit. Just how disagreeable are we talking, here? :)


Thanks! Welcome to the club of unofficial forks! Who cares what anybody else agrees with! :)

(I believe the HN codebase expires reply links after 14 days, as a spam control measure.)


For some reason I can't reply higher than this, but yeah, that was a side-project of mine that's in no way official.

If you looked at it closely, I made many changes in the spirit of getting something running that PG (and you!) would likely not agree with.

I know nothing of PG's plans for future releases, and I'd not be the one doing them.

Sorry!


Yes, I was phrasing it poorly. I meant that since just reusing the binding sometimes doesn't work, the idiom has evolved to always rebind. Even for non-nil lists, and even for destructive operations that are safe to just operate on like nconc. But I'm not a huge expert on what the idioms are :)

"But there's a reason lisp does seemingly the wrong thing. According to the spec, nconc skips empty arguments (http://www.lispworks.com/documentation/HyperSpec/Body/f_ncon...) Reading between the lines, I'm assuming this makes sense from a perspective in lisp where we communicate even with 'destructive' operations through their return value."

This isn't just an idiomatic thing. Since nil is immutable, (nconc a '(34)) couldn't change a if it tried.

And whereas Python's list.append() mutates a single object, Lisp operations like 'nconc and 'nreverse destructively create a whole tree of objects. Particularly in the case of 'nreverse, the new tree's root isn't specified to be any particular object the caller has seen before, so the caller has to get a reference to it somehow: as the return value.

Incidentally, the spec for 'nconc leaves less unspecified, so the programmer doesn't actually need to use the return value. Given a non-returning version, they can reconstruct a returning version like so:

  ; Arc code
  (def nconc args
    (do1 (find idfn args)
         (apply nconc-with-no-return-value args)))
So the return value of 'nconc probably is an idiomatic thing, for consistency with 'append and 'nreverse.

Thanks!

Arc does have similar gotchas with quote: http://arclanguage.org/item?id=10248 (one of my earliest posts on this forum)


Ooh, cool discussion. I really like the connection you made to nconc.

At least Arc does the default properly:

  arc> (def f (n (o tbl (obj))) (= tbl.n (* n 2)) tbl)
  #<procedure:zz>
  arc> (f 1)
  #hash((1 . 2))
  arc> (f 2)
  #hash((2 . 4))

Ha! I guess someone jumped the gun.

D'oh, the README no longer says anything about public code releases of HN: https://github.com/HackerNews/HN/commit/64e663f5977033294848...

The rarc-public repo is no longer visible on github.

Yep, that's the last release. It's why I'm so excited for this github repo.

Yeah, I know, seems old though like 2009.

Nope, we only find out when you do.

(We here maintain various forks of the last release from 4 years ago: http://github.com/arclanguage)

More