| 1 |
--- |
| 2 |
title: BSN Syntax Improvements. |
| 3 |
pull_requests: [25318] |
| 4 |
--- |
| 5 |
|
| 6 |
BSN landed with a few idiosyncrasies that caused friction in practice. We made some changes to BSN's syntax this cycle in the interest of improving its ergonomics and clarity. |
| 7 |
|
| 8 |
All scene references now require `@` prefixes: |
| 9 |
|
| 10 |
```rust |
| 11 |
// Before |
| 12 |
bsn! { |
| 13 |
scene_variable |
| 14 |
scene_function() |
| 15 |
{scene_expression} |
| 16 |
} |
| 17 |
|
| 18 |
// After |
| 19 |
bsn! { |
| 20 |
@scene_variable |
| 21 |
@scene_function() |
| 22 |
@{scene_expression} |
| 23 |
} |
| 24 |
``` |
| 25 |
|
| 26 |
This freed us up to make component values _much_ easier to work with. |
| 27 |
|
| 28 |
```rust |
| 29 |
// Before |
| 30 |
bsn! { |
| 31 |
template_value(component_variable) |
| 32 |
template_value(component_function()) |
| 33 |
} |
| 34 |
|
| 35 |
// After |
| 36 |
bsn! { |
| 37 |
component_variable |
| 38 |
component_function() |
| 39 |
} |
| 40 |
``` |
| 41 |
|
| 42 |
Enums no longer require `VariantDefaults` or `FromTemplate`, provided they implement `Default` and `Clone`: |
| 43 |
|
| 44 |
```rust |
| 45 |
// Before |
| 46 |
#[derive(Component, Default, Clone, VariantDefaults)] |
| 47 |
enum Foo { |
| 48 |
A { x: u32, y: u32 }, |
| 49 |
#[default] |
| 50 |
B, |
| 51 |
} |
| 52 |
|
| 53 |
bsn! { |
| 54 |
Foo::B |
| 55 |
} |
| 56 |
|
| 57 |
// After |
| 58 |
#[derive(Component, Default, Clone)] |
| 59 |
enum Foo { |
| 60 |
A { x: u32, y: u32 }, |
| 61 |
#[default] |
| 62 |
B, |
| 63 |
} |
| 64 |
|
| 65 |
bsn! { |
| 66 |
Foo::B |
| 67 |
} |
| 68 |
``` |
| 69 |
|
| 70 |
If you were using an enum that didn't support `VariantDefaults`, you can remove the `template_value` wrapper: |
| 71 |
|
| 72 |
```rust |
| 73 |
// Before |
| 74 |
bsn! { |
| 75 |
template_value(Foo::A) |
| 76 |
} |
| 77 |
// After |
| 78 |
bsn! { |
| 79 |
Foo::A |
| 80 |
} |
| 81 |
``` |
| 82 |
|
| 83 |
The "variant defaults" pattern, which relied on defining individual "default" constructors for each variant is what allowed "individual enum field value patching" (ex: `VariantDefaults` and `FromTemplate` would define `Foo::a_default()` and `Foo::b_default()` in the example above). This is no longer supported, as the weirdness factor (and Rust ecosystem compatibility challenges) were too costly. When working with enums in BSN, you must now specify each field in the enum, just like you would in normal Rust (which doesn't have support for individual enum variant defaults). |
| 84 |
|
| 85 |
```rust |
| 86 |
// Before (y field is initialized to its default value) |
| 87 |
bsn! { |
| 88 |
Foo::A { x: 1 } |
| 89 |
} |
| 90 |
|
| 91 |
// After (y field must be manually specified) |
| 92 |
bsn! { |
| 93 |
Foo::A { x: 1, y: 0 } |
| 94 |
} |
| 95 |
``` |
| 96 |
|
| 97 |
The "builder pattern" previously required a `template_value` wrapper. This can now be removed: |
| 98 |
|
| 99 |
```rust |
| 100 |
// Before |
| 101 |
bsn! { |
| 102 |
template_value(Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y)) |
| 103 |
} |
| 104 |
// After |
| 105 |
bsn! { |
| 106 |
Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y) |
| 107 |
} |
| 108 |
``` |
| 109 |
|
| 110 |
Additionally, you can now remove the `template_value` wrapper in cases like this: |
| 111 |
|
| 112 |
```rust |
| 113 |
// Before |
| 114 |
bsn! { |
| 115 |
template_value(node.clone()) |
| 116 |
} |
| 117 |
// After |
| 118 |
bsn! { |
| 119 |
node.clone() |
| 120 |
} |
| 121 |
``` |
| 122 |
|
| 123 |
In general, you should now be able to remove all `template_value` instances from your BSN declarations! |
| 124 |
|
| 125 |
List syntax in BSN has also been improved. BSN used to use commas to separate entities, with optional `()` around entities to make the boundaries clearer. This resulted in a lot of syntax noise, line noise, and over-indentation: |
| 126 |
|
| 127 |
```rust |
| 128 |
bsn! { |
| 129 |
Node |
| 130 |
Children [ |
| 131 |
( |
| 132 |
#OkButton |
| 133 |
@button("Ok") |
| 134 |
), |
| 135 |
( |
| 136 |
#CancelButton |
| 137 |
@button("Cancel") |
| 138 |
), |
| 139 |
] |
| 140 |
} |
| 141 |
``` |
| 142 |
|
| 143 |
To avoid this, many developers opted for this syntax instead, which made it very hard to visually distinguish entities: |
| 144 |
|
| 145 |
```rust |
| 146 |
bsn! { |
| 147 |
Node |
| 148 |
Children [ |
| 149 |
#OkButton |
| 150 |
@button("Ok"), |
| 151 |
#CancelButton |
| 152 |
@button("Cancel"), |
| 153 |
] |
| 154 |
} |
| 155 |
``` |
| 156 |
|
| 157 |
BSN now uses `--` to separate entities: |
| 158 |
|
| 159 |
```rust |
| 160 |
bsn! { |
| 161 |
Node |
| 162 |
Children [ |
| 163 |
#OkButton |
| 164 |
@button("Ok") |
| 165 |
-- |
| 166 |
#CancelButton |
| 167 |
@button("Cancel") |
| 168 |
] |
| 169 |
} |
| 170 |
``` |
| 171 |
|
| 172 |
This gives us the best of all worlds: entities are visually distinct, and there is no over-indentation, line noise, or syntax noise. Both `()` and `,` have been deprecated in this context. |
| 173 |
|
| 174 |
Using `[]` and `()` for `bsn_list!` (and `bsn!`) is now discouraged / warned against (ex: `bsn_list! []`), as it can result in poor rustfmt autoformatting. Instead, use `bsn_list! {}`, which is the only syntax that `rustfmt` won't touch. Don't worry, we |
| 175 |
plan to build a BSN auto-formatter! |
| 176 |
|
| 177 |
```rust |
| 178 |
bsn_list! { |
| 179 |
#Ok @button("Ok") |
| 180 |
-- |
| 181 |
#Cancel @button("Cancel") |
| 182 |
} |
| 183 |
``` |