Feedback request: Another take on label arguments

So, I've come up with a simple implementation of labeled arguments (also known as named arguments, keyword arguments, etc.) for functions. For the moment, it is a simple macro, it doesn't have a syntax but it is strongly-typed and it supports reordering arguments and optional arguments.

I have a few ideas to improve it, but I'd be interested on feedback on this early draft:


Instead of:

fn do_rect(width: f64, height: f64) {
    // ...
}
const WIDTH : f64 = 10.0;
const HEIGHT: f64 = 30.0;
do_rect(WIDTH, HEIGHT); // Oops, confused width and height.

Let's do this:

#[macro_use]
extern crate named_args;
#[macro_use]
extern crate typed_builder;

labeled!(do_rect({width: f64, height: f64}) {
  // ...
});

fn main() {
  const WIDTH : f64 = 10.0;
  const HEIGHT: f64 = 30.0;
  do_rect::labeled()
      .width(WIDTH)   // No confusion here. Order doesn't matter.
      .height(HEIGHT) // No confusion here. Order doesn't matter.
      .build()
      .call();
}

We can even add optional arguments:

#[macro_use]
extern crate named_args;
#[macro_use]
extern crate typed_builder;

labeled!(do_rect({width: f64, height: f64, opacity : f64 = 1.0}) {
  // ...
});

fn main() {
  const WIDTH : f64 = 10.0;
  const HEIGHT: f64 = 30.0;
  // With the optional argument:
  do_rect::labeled()
      .width(WIDTH)   // No confusion here. Order doesn't matter.
      .opacity(0.)    // Order still doesn't matter.
      .height(HEIGHT) // No confusion here. Order doesn't matter.
      .build()
      .call();

  // Or without:
  do_rect::labeled()
      .width(WIDTH)   // No confusion here. Order doesn't matter.
      .height(HEIGHT) // No confusion here. Order doesn't matter.
      .build()
      .call();
}

Omitting a required field will cause a type error:

#[macro_use]
extern crate named_args;
#[macro_use]
extern crate typed_builder;

labeled!(do_rect({width: f64, height: f64, opacity : f64 = 1.0}) {
  // ...
});

fn main() {
  const WIDTH : f64 = 10.0;
  const HEIGHT: f64 = 30.0;

  do_rect::labeled()
      .width(WIDTH)   // No confusion here. Order doesn't matter.
      .build()        // <-- Build error here.
      .call();
}

Whaddaya think?

Repo is here, if you wish to help out!

edit Who's the bumbling fool who called this "positional arguments"? Couldn't be me, right?

1 Like

A few ideas I'd like to implement (but they'll take a little more effort on my part):

  1. Reduce the number of function calls.

I'd like to write

do_rect::width(WIDTH)
   .height(HEIGHT)
   .call();
  1. Introduce syntactic sugar
label!(do_rect { width: WIDTH, height: HEIGHT });
  1. Extend this to methods and traits.
  2. Improve error messages.
  3. If people like it, perhaps turn it into a RFC for the language!

That is very cunning but I'm not drawn to it.

It seems to add a lot of verbosity and syntactic "line noise" to solve a problem I don't have.

My IDE already tells me what parameters I need where.

Even preliminary tests, which are so easy in Rust, would show up any confusion.

It's the kind of complexity I would rather not be standardized and used in any Rust code I read.

Correct me if I'm being stupid, but aren't positional arguments the exact opposite of named/labeled arguments? As in, you pass them by their position in the argument list, not by their name.

1 Like

I'm not Yoric but yes, that is the way I have always understood it as well.

Correct me if I'm being stupid, but aren't positional arguments the exact opposite of named/labeled arguments? As in, you pass them by their position in the argument list, not by their name.

Fixed, thanks!

That makes sense.

Note that I'm prototyping the semantics/implementation. If it were to be standardized, this would certainly be with syntactic sugar, for instance

do_rect(.width = WIDTH, .height = HEIGHT); 

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.