vickenty/perl-xs

Procedural function attribute macro, Optional argument handling

dnorman opened this issue · 4 comments

Objectives:

  1. Export functions to perl which contain optional arguments.
  2. Make argument handling more magical
  3. Move toward more idiomatic rust conventions for function binding

At present, the xs! macro is unable to handle Option arguments which may be omitted by the perl caller.

I propose that we do away with the xs! macro_rules in favor of a procedural function attribute macro, similar to wasm_bindgen or the pyo3 crate.

Proposed syntax:

//lib.rs:
mod bar;
mod blah;
perl_base_module!("Foo")

//bar.rs
#[perl]
fn alpha (maybe: Option<Something>){}

//blah.rs
#[perl]
fn beta (definitely: Something){}

By doing this as a procedural macro, this will also improve the ability to perform smarter argument handling / coercion, including derive structs, optional context argument, etc.

The goal here is to have a perfectly ordinary rust function be callable from within perl with little or no special accommodation for the perl guts, at least in some circumstances.

How a procedural macro can improve argument coercions?

If nothing else, macro_rules style macros are too unwieldy to make sufficient improvement (at least they are for me) and I think they are also incapable of branching logic, as is required for Option<T> vs T or other such situations. I fully intend to implement this myself, and am not really asking for anyone else to do it per se. I was merely hoping to use this ticket as a way to socialize the idea and get feedback :)

I think it would be super cool to utilize a wasm_bindgen style attribute macro, such that the same rust function is able to accommodate rust callers and Perl callers.

It could more tightly integrate with the struct argument unpacking derive macro I wrote earlier as well.

I wasn't really arguing against improving syntax; I agree that the current xs! macro is quite bulky.

But some of the improvements you want to make by switching to procedural macro don't make much sense to me.

Functions require all formal parameters to be specified quite deliberately, inspired by the behavior of xsubpp and perl's own subroutine signatures. If anything, I'd like to make it even more strict and die when too many parameters are supplied too (to make it even more like xsubpp and perl signatures). This is different story for another issue, but the point here is that this isn't really a limitation of the macro itself.

  • Handling Option vs other types is also not a job for a macro. AIUI procedural macros are expanded before name resolution, so it would be impossible to tell std Option from one defined in another crate. Trait system is the tool for this job, and some Option types are already supported (e.g. `Option).

Otherwise, sounds cool! :) I'm interested to see how this pans out.

One of the situations I'm trying to accommodate is this:

#[perlxs]
fn test ( a: String, b: Option<String> ){
...
}

Valid perl-side calling scenario 1:

test("Meow");

Valid perl-side calling scenario 2

test("Meow","Woof")

if fn test accepts Option<T> as a positional argument, then the binding code ( presently defined inside !xs should call the function with None instead of croaking with "not enough arguments ..."

Making this change also provides an opportunity to:

  • Make the perl context object an optional function argument – fn test(a: String) vs fn test(ctx: Context, a: String)
  • Handle argument types which impl FromPerlKV
  • Improve return type handling
  • More readable macro logic will encourage community improvement
  • Probably offers other benefits too :)