7,404 karma · joined February 19, 2007
let ast = parse_macro_input!(input as Foo);
Where foo is some type defined by the `syn` crate and there's one for every production in Rust's grammar. But neither are you limited to those. You can also extend or replace that grammar as you see fit, but in that case the added parsing is on you.Just as use of `eval` tends to be discouraged in Lisp land, it's not something that a Haskell or Rust programmer would often reach for. But in Haskell, if you really want it, GHC has an API and you can have the whole power of the compiler available to you at runtime. This isn't really a language feature per se, it's literally just linking in the whole compiler and calling it like an ordinary library. I'm not aware of anything similar to that in Rust but I haven't really looked. However, if you're only trying to generate code and not JIT-compile or execute it, the same Rust crates that support compile-time AST manipulation (like `syn` and `quote`) can equally well be used at runtime.
Done properly? I can't speak to camlp4, but at least in the case of Haskell and Rust, certainly. Incidentally I just had my first occasion to write a Rust procedural macro last weekend. I had a significantly complex transformation written and working in half a day, learning curve included, and I found it all pretty frictionless.
Camlp4, Template Haskell, and Rust procedural macros all serve as counterexamples to this claim.
IRBs are like the TSA. Imposing annoyance and red tape on the honest vast-majority while failing to actually filter the 0.0001% of things they ostensibly exist to filter.
fn concat<T,M,N>([T; M], [T; N]) -> [T; M+N]Edit: check for me whether just calling lrint(x) works. The manpage doesn't specify that lrint() will set FE_INEXACT, but it seems weird to me that it wouldn't.
When you say,
> But I also believe it will be possible to write efficient implementations based on Bel, by adding restrictions.
I'm having trouble picturing what such restrictions would look like. The difficulty here is that, although you speak of axioms, this is not really an axiomatic specification; it's an operational one, and you've provided primitives that permit a great deal of introspection into that operation. For example, you've defined closures as lists with a particular form, and from your definition of the basic operations on lists it follows that the programmer can introspect into them as such, even at runtime. You can't provide any implementation of closures more efficient than the one you've given without violating your spec, because doing so would change the result of calling car and cdr on closure objects. To change this would not be a mere matter of "adding restrictions"; it would be taking a sledgehammer to a substantial piece of your edifice and replacing it with something new. If closures were their own kind of object and had their own functions for introspection, then a restriction could be that those functions are unavailable at runtime and can be only be used from macros. But there's no sane way to restrict cdr.
A true axiomatic specification would deliberately leave such internals undefined. Closures aren't necessarily lists, they're just values that can be applied to other values and behave the same as any other closure that's equivalent up to alpha, beta, and eta conversion. Natural numbers aren't necessarily lists, they're just values that obey the Peano axioms. The axioms are silent on what happens if you try to take the cdr of one, so that's left to the implementation to pick something that can be implemented efficiently.
Another benefit of specifying things in this style is that you get much greater concision than any executable specification can possible give you, without any loss of rigor. Suppose you want to include matrix operations in your standard library. Instead of having to put an implementation of matrix inversion into your spec, you could just write that for all x,
(or
(not (is-square-matrix x))
(singular x)
(= (* x (inv x))
(id-matrix (dim x))))
Which presuming you've already specified the constituent functions is every bit as rigorous as giving an implementation. And although you can't automate turning this into something executable (you can straightforwardly specify a halting oracle this way), you can automate turning this into an executable fuzz test that generates
a bunch of random matrices and ensures that the specification holds.If you do stick with an operational spec, it would help to actually give a formal small-step semantics, because without a running implementation to try, some of the prose concerning the primitives and special forms leaves your intent unclear. I'm specifically puzzling over the `where` form, because you haven't explained what you mean by what pair a value comes from or why that pair or its location within it should be unique. What should
(where '#1(#1 . #1))
evaluate to? Without understanding this I don't really understand the macro system.* Give an error bound like 45.73490534578° (±0.00002°) and indicate in prose that this is a 2σ bound.
* Put non-significant figures in parenthesis, like 45.73490(534578)° (EDIT: possibly I've misinterpreted this one when I've seen it, see logfromblammo's reply)
* Put a bar over the last significant figure, like 45.73490̄534578 (hopefully this one renders properly when I post this... (EDIT: nope))