Arc Forumnew | comments | leaders | submitlogin
2 points by aw 2452 days ago | link | parent | on: Lexically scoped macros?

> allowing local variables to override macros

Yes, that's easy to do. At the top of ac-call in ac.scm where it checks ac-macro?, also check to see that "fn" isn't a symbol and in env.

5 points by zck 2452 days ago | link | parent | on: Lexically scoped macros?

Another solution to this would be to have an actual namespace mechanism. For example, this would be the Clojure for a similar test file:

    (ns anarki.foo-tests
      (:require [zck.unit-test :as t]))
    
    (t/suite cut
             (t/test finds-element-in-list
                     (t/assert-same '(3 4 5) (cut '(1 2 3 4 5) 2))))
It's another benefit we'd get by having namespacing.
3 points by akkartik 2452 days ago | link | parent | on: Lexically scoped macros?

I don't think this has come up before. An f-expr based Lisp would automatically have this property (at much performance cost). But we haven't discussed lexical macros as a separate construct.

I feel like something kinda related has come up a couple of times before: allowing local variables to override macros. I think there was even a one-line patch to ac.scm at some point. But it must have had some issue since it's not in Anarki :)

Maybe this thread brings up some ideas? http://arclanguage.org/item?id=3056

2 points by hjek 2458 days ago | link | parent | on: Show Arc: Debate (very alpha)

Thanks for posting that. (The issue is not specific to Windows.)

I've switched from Notabug to Gitlab now, because Notabug is being painfully slow.

https://gitlab.com/hjek/undebatable/issues/1

3 points by jsgrahamus 2464 days ago | link | parent | on: Show Arc: Debate (very alpha)

Posted issue for Windows 10 Home
3 points by waterhouse 2464 days ago | link | parent | on: Bugs and failures

It should be possible to get the continuation from the point of failure and the dynamic variables from the failing thread (basically: the stack), the same information from any other running threads, and the set of global variables (this at least can be gotten with (namespace-mapped-symbols)), and trace the graph of reachable objects from there, and serialize it all to a file. I don't know if Racket provides the ability to do all that, though; for one thing, I don't know if there's a way to access the variables saved in a closure (from outside the closure).[1] (Maybe using unsafe operations could do that.) Since tracing the graph of objects is exactly what a GC does, and a proper moving GC has to be able to learn the type of every object and where all the pointers are, it must have that functionality, whether or not it's exposed. (I think it should be exposed, of course.)

Barring that, it's possible that the gdbdump rocketnia points at is the easiest way to do it in Racket.

Also, I guess if you're using the FFI at all (which, say, any GUI program would do), then you do need the full core dump if you want to get the state of the C libraries you're using.

[1] https://docs.racket-lang.org/reference/procedures.html isn't promising. https://docs.racket-lang.org/web-server-internal/closure.htm... provides wrapper macros to make serializable lambdas, implying that there is no way to serialize normal lambdas, which is unfortunate.

3 points by aw 2464 days ago | link | parent | on: Bugs and failures

That's insanely clever to define a callable custom type to handle the case of calling the failfn without a fail handler. I'm impressed.
3 points by rocketnia 2465 days ago | link | parent | on: Bugs and failures

In Lathe and in the first version of Penknife (written in Arc), I was calling this kind of feature "failcall." A function could be called with `failcall` to handle its failures, or it could be called normally, in which case its failures would be promoted to errors automatically.

Your example of using Racket parameters leads to a slight difference in behavior from what I would want. Suppose the code in the body contains a call to some function that in turn makes a normal call to another function which fails. With the Racket parameter technique you talk about, the parameter binding would still be in scope at that point, so the failure would be caught, even though I think the author of that normal function call would have expected its unhandled failures to be promoted to bugs.

I remember thinking Racket parameters would be useful, but the technique I ended up with didn't use them at all. There's a full-featured implementation in the Lathe arc/ folder's failcall.arc[1], but here's a short proof of concept for Anarki:

  ; In this example, a "failfn" is a tagged single-argument function
  ; that returns (list t <success-val>) or (list nil <failure-val>).
  (mac failfn (x . body)
    `(annotate 'failfn (fn (,x) ,@body)))
  
  ; To call a function in a way which handles failures, we pass in an
  ; argument and an `on-fail` handler like so. This can be used with
  ; normal functions too, which just never fail.
  (def failcall (f x on-fail)
    (if (isa f 'failfn)
      (let (succeeded val) rep.f.x
        (if succeeded
          val
          (on-fail val)))
      f.x))
  
  ; When a failfn is called normally, it behaves as though it was
  ; failcalled with a handler that always produces an error.
  (defcall failfn (f x)
    (failcall f x
      (fn (failure-val)
        (err:+ "Failed with " (tostring:write failure-val)))))
  
  ; We define an example failfn. We can't use `def` for this since it
  ; defines a normal function.
  (= failure-prone-sqrt
    (failfn x
      (if (< x 0)
        (list nil "Tried to take the square root of a negative number")
        (list t (sqrt x)))))
  
  
  
  arc> (failure-prone-sqrt 4)
  2
  arc> (failure-prone-sqrt -4)
  Failed with "Tried to take the square root of a negative number"
    context...:
     /path/to/anarki/ac.rkt:1327:4
  
  arc> (failcall failure-prone-sqrt 4 idfn)
  2
  arc> (failcall failure-prone-sqrt -4 idfn)
  "Tried to take the square root of a negative number"
  arc> (failcall sqrt 4 idfn)
  2
  arc> (failcall sqrt -4 idfn)
  0+2i
The REPL transcript shows me calling a failfn using a normal call, calling a failfn using a failcall, and calling a normal function using a failcall. The only case that causes an actual error is when the failfn fails and there was no handler to catch it.

Obviously, a more full-featured approach would allow failcalls of arity other than one. And this `failcall` syntax doesn't have the convenient kind of pattern-matching syntax your `onfail` macro does, but that kind of thing could be built as a layer over the top of this example; I'm just keeping the example small.

Racket is just as capable of this technique as Anarki is. Instead of an `annotate` tagged value, the Racket version would use a struct, and instead of `defcall`, it would use the `prop:procedure` structure type property.

[1] https://github.com/rocketnia/lathe/blob/7127cec31a9e97d27512...

---

As far as making core dumps goes, I've never tried this, but it looks like `gdbdump` might be able to do it for Racket programs on Linux.[2] There's also a Racket built-in called `dump-memory-stats`,[3] which at least in Racket 7.0 appears to give a summary of how many objects of certain kinds are in memory.

[2] https://docs.racket-lang.org/gdbdump/index.html

[3] (https://docs.racket-lang.org/reference/garbagecollection.htm...)

2 points by adas 2465 days ago | link | parent | on: Clojure Anaphoric Macros

Didn't consider a double `%then` or `%else`, thanks. Raises some interesting problems.

Generally the plan is to have a special character control whether it gets bound or inlined, probably `!`. So say `%test!` would get inlined like right now, while using `%test` would bind (and doing both would also be possible). But for a `%then` you'd generally only want to bind if there's 2+ so I could count usages instead.

2 points by akkartik 2465 days ago | link | parent | on: Clojure Anaphoric Macros

Thanks for that example. I see now that you mostly only need to worry about multiple evaluation for the `%test` branch; `%then` and `%else` should be exclusive anyway, and I'm not concerned about the growth in macroexpanded code when duplicating a few s-expressions.

You could still have repeated use within a '%then' or '%else' block:

    (aif (test)
      (something)
      (do %then %then))
But it should suffice to perform one evaluation in each branch. Cool! That seems simpler than some of the alternatives I'd been thinking about. I'd try to evaluate everything ahead of time and then realize that I shouldn't run `%else` if the `%test` returns a truthy value.
3 points by adas 2465 days ago | link | parent | on: Clojure Anaphoric Macros

Author here. Glad to see some interest, sometimes I lurk here and now I feel bad not having submitted here myself.

Interestingly the "%else" would actually be cataphoric, as you refer to what comes next rather than before. So "co-referential macros" would be more fitting if you want to stick with the linguistics analogy. But that'd be too exotic of a term.

And yes as akkartik notes, it causes multiple evaluations right now, mostly just laziness and indecision on my part. I'll probably be giving control over this. Here's a real example of code where you actually want current behavior:

  (aif (ns-resolve *ns* 'specs)
    (let [c (compile spec :name name)]
      (swap! (var-get %test) update (:property c) set/union #{c}))
    (do (intern *ns* 'specs (atom {}))
         %then))
Would love to hear if anyone has some crazy ideas. Beyond just conditionals too.
2 points by aw 2465 days ago | link | parent | on: Bugs and failures

A failure that is unexpected and unplanned for is a bug. Thus it's a bug if a file doesn't exist and my code doesn't handle that situation.

The boundary is what I want to happen in response to a bug vs. a failure. When I hit a bug, an actual bug, I want to capture the entire state and history of my program, to the fullest extent possible, so that I can find out why the bug occurred. I don't care if this a core dump is GBs in size or might be expensive to generate. If a bug occurs I want all possible information that might help me, everything that the language runtime can produce.

For failures, for expected failures, for failures I handle, I don't need to capture anything. I don't even need a stack trace. I don't need the language runtime to generate a stack trace every time I hit an expected, handled failure.

Existing languages don't allow me to do this. At the point where for example the "file not found" exception is being thrown there isn't enough information to tell whether that's a failure or a bug, so they have to be handled the same.

3 points by akkartik 2465 days ago | link | parent | on: Bugs and failures

I'm not sure I grok the precise boundary you're drawing here.

It seems clear that (car 10) is always a bug, so I'm with you there. However, non-existent files may be bugs in some situations. Perhaps you're just proposing giving programmers two distinct labels to use with discretion? If so I shouldn't get hung up on precise examples.

Are all unhandled failures bugs?

4 points by i4cu 2467 days ago | link | parent | on: Arc REPL in emacs using cider-mode

> There's no audio

There's awesome audio... I watched it a second time just for the audio!

5 points by i4cu 2467 days ago | link | parent | on: Arc REPL in emacs using cider-mode

For anyone else who might be interested:

nRepl https://github.com/nrepl/nrepl

Cider https://docs.cider.mx/en/latest/

Cider-nRepl https://github.com/clojure-emacs/cider-nrepl

5 points by akkartik 2467 days ago | link | parent | on: Arc REPL in emacs using cider-mode

Looks wonderful! Thanks for exposing me to some new keywords ("nrepl", "cider-mode")
2 points by i4cu 2467 days ago | link | parent | on: Arc REPL in emacs using cider-mode

> I just wanted to throw together a quick demo of the Arc tooling I've been working on.

To what end, might I ask? i.e. are you planning to contribute this to the arc community? Is this something the community can leverage with their own tooling?

I could have other questions, but if it's not something I can use or look at then they become somewhat pointless (the questions, that is).

4 points by shawn 2467 days ago | link | parent | on: Arc REPL in emacs using cider-mode

There's no audio, so it's probably a bit hard to follow. I just wanted to throw together a quick demo of the Arc tooling I've been working on.

I implemented the nREPL protocol in Arc. This video shows cider-mode interacting with it (via `cider-connect`).

3 points by evanrmurphy 2469 days ago | link | parent | on: Try Arc down, will be back up soon

Hi Kartik, it's Arc 3.1. Cheers.
3 points by akkartik 2469 days ago | link | parent | on: Try Arc down, will be back up soon

Thanks, Evan!

Remind me, is this running Arc 3.1 or Anarki? Some third option?

3 points by evanrmurphy 2469 days ago | link | parent | on: Try Arc down, will be back up soon

Ok it's back online now.

I ended up just getting rid of the contact form, and now link more prominently to these forums.

3 points by i4cu 2471 days ago | link | parent | on: Clojure Anaphoric Macros

> do these macros cause multiple evaluation

from the read-me:

"Note that as of right now the symbols are replaced by copying in the expression it references, not by binding to a common variable. Hence not suitable for using with expressions that cause side-effects or involve a lot of computation. That will be changed soon."

I presume, by reading this, these expressions will be evaluated each time they are triggered within the operation, so the answer is - yes they will. But, as the read-me also suggests, this is a "WORK IN PROGRESS" and the author has stated intentions to assign variables, which would solve the issue.

3 points by akkartik 2472 days ago | link | parent | on: Clojure Anaphoric Macros

Staring at these examples again, another thought occurs to me: do these macros cause multiple evaluation, or are the equivalences above just loose? Can anybody tell? I can't tell just from skimming the implementation.

If they're doing multiple evaluations they're a lot less useful than Paul Graham's original anaphoric macros even if they seem superficially more powerful/expressive.

2 points by hjek 2472 days ago | link | parent | on: Clojure Anaphoric Macros

If you include * whitespace * between words and stars, they are not parsed as an emphasized section.
2 points by hjek 2473 days ago | link | parent | on: Clojure Anaphoric Macros

Racket doesn't have anaphoric macros as part of the core language (although there is a module for that), so that's a bit difficult getting used to when coming from Arc, but I find that pattern matching[0] can be used to the same effect:

    (match 123
       ((and (? even?) it) (~a it " is even"))
       (it (~a it " is odd")))
[0]: https://docs.racket-lang.org/reference/match.html
5 points by akkartik 2474 days ago | link | parent | on: Clojure Anaphoric Macros

"In `acond`, `aif` and `awhen`, `%test` or `%t` gets replaced with the 'test' form. `%then` gets replaced by the 'then' form, and `%else` by the 'else' form."

    (aif 9 (+ 9 %else) (+ 10 %test))
    ; => (if 9 (+ 9 (+ 10 9)) (+ 10 9))
"If nested you can access the previous level by doubling the first letter of the symbol. For example, '%ttest' would get you the previous [containing?] 'test' form, while '%eeelse' would get you the 'else' form 2 levels up. In the `aand` and `aor` macros you can reference arguments by using a symbol of form `<star><num>` where 'num' is the 1-index of the argument. Previous levels are accessed by doubling the `<star>` character. So the second 'test' form of an `aand` can by accessed with `<star>2` and the third argument of the previous [containing?] `aand` would be `<star><star>3`." [Working around the crappy pseudomarkdown here.]

    (aand (+ 30 20) *1)
    ; => (and (+ 30 20) (+ 30 20))

    (aand 1 2 "third" (aand 33 **3))
    ; => (and 1 2 "third" (and 33 "third"))
Utterly bonkers, and I love the hack :)
3 points by i4cu 2474 days ago | link | parent | on: Clojure Anaphoric Macros

Saw this on reddit and given that it's derived from Arc I thought I'd post it over here.

Certainly these are much more involved than my functions. My aif for example is:

  (defmacro aif [expr & body]
    `(let [~'it ~expr]
       (if ~'it
         (do ~(first body))
         (do ~@(next body)))))
Actually, when I started in Clojure I was using these anaphoric operations a lot, but most of my code has moved away from them (for no particular reason, I just haven't needed them much I guess).

HN link: https://news.ycombinator.com/item?id=18647562

Very interesting write up. All that is left is the due date of conversion to Arc..
2 points by hjek 2482 days ago | link | parent | on: Show Arc: Debate (very alpha)

> My idea of graceful degradation is falling back to a message that says "sorry, not going to happen!" :)

Hilarious! A degradation for sure but the graceful part of is questionable.

More