Arc Forumnew | comments | leaders | submitlogin
3 points by rocketnia 25 days ago | link | parent

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
          (on-fail val)))
  ; 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)
  arc> (failure-prone-sqrt -4)
  Failed with "Tried to take the square root of a negative number"
  arc> (failcall failure-prone-sqrt 4 idfn)
  arc> (failcall failure-prone-sqrt -4 idfn)
  "Tried to take the square root of a negative number"
  arc> (failcall sqrt 4 idfn)
  arc> (failcall sqrt -4 idfn)
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.



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.


[3] (

3 points by aw 25 days ago | link

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.