Builder syntax

you've heard of builder pattern, but... let's say you have

let x = Foo::new();
x.thing(foo);
x.thing(bar);
x.thing(zed);

actually for better effect let's say you have

let x = { let mut temp = HashMap::new();
temp.insert("foo", "bar");
temp.insert("baz", "zed");
temp };

now, hashmaps are clearly not builders; but what if they could be used as if they were? we'd like to propose the following syntax:

let x = HashMap::new()(.insert("foo", "bar"))(.insert("baz", "zed"));

as exact sugar for the following:

let x = {
  let mut temp = {
    let mut temp = HashMap::new();
    temp.insert("foo", "bar");
    temp
  };
  temp.insert("baz", "zed");
  temp
};

this is actually pretty easy to parse as the token for this is really (. and not some arbitrary thing. but anyway. thoughts? this would also provide ergonomics for using certain types of iterators (but we don't have a good example for that).

Hmmm... this

HashMap::new()(.insert("foo", "bar"))(.insert("baz", "zed"));

looks pretty weird to me personally. If a type is commonly built like this, why not just have a builder?

2 Likes

What about borrowing inspiration from the vec! macro and creating a new hashmap! macro that might look something like:

let map = hashmap![
    "foo": "bar",
    "baz": "zed"
];

(Actually, I'm about 90% sure I've seen this macro before, but I can't remember where)

2 Likes

maplit provides a couple macros for building collections:

let map = maplit::hashmap!{
    "a" => 1,
    "b" => 2,
};

I agree there should be hashmap macro in std.

7 Likes

We took inspiration from our own query language here. Our query language doesn't really have variables, instead you're supposed to use these things that run a query on the current value but leave said value for the next part to query again.

It means you can have something like

->'projects':$dict
  ->commit[:?$str:?$commit]:?$dict
    ->url[:?$str:?$uri]:?$dict
      ->branch:?$dict
        (->active'active'?:?$bool)
        (->federate'federate'?:?$bool)?

and extract both "active" and "federate" in one go.

While it's only a query language, the syntax elements do have the following properties:

  1. They can have side effects
  2. They can accept a value
  3. They can return a value

Which means they're all basically functions! But sometimes you want to apply side effects but the return value is useless to you, and that's the exact use-case this syntax solves.

Are you proposing a new expr(.expr) syntax for consecutively calling methods? I don't see any need for a such syntax, your hashmap example is already solvable by macros as other said. Can you give better examples about why this can be usefull and worths introducing a new syntax?

3 Likes

This is a well trodden problem: It even has an implementation as a crate https://crates.io/crates/cascade

Previous motivations:

Previous discussions:

5 Likes

yeah you can use ? in it. e.g. with a hypothetical slice iterator with a split method that splits the slice at the current iterator position, you could do something like iter(.next()?).split(). since arbitrary iterators aren't really splittable, this solves it quite easily! (altho perhaps not cleanly?) (and, if instead you wanted to make arbitrary iterators splittable, like say iter.skip(1).split()... how would you implement something like that?)

Prior art: Dart's method cascade syntax.

2 Likes