[RFC] Allow passing components directly as arguments, and support composition
There's two folds for this RFC. Firstly, I think it would be good for beginners to not have to think about passing arrays to add. It should accept add(body(), pos(), area()). Secondly, as a side-effect of trying to be backwards incompatible, you can now compose components and use them in objects without having to learn array spread syntax. To fully support this form of composition, we can allow infinitely nested arrays.
Before
const playerComponents = [
sprite('player'),
body(),
area(),
pos(),
"player",
];
const evilPlayerComponents = [
...playerComponents, // or spreading later when calling add([...playerComponents, ...evilPlayerComponents])
color(0, 0, 0),
origin("left"),
];
add(evilPlayerComponents);
After
const playerComponents = [
sprite('player'),
body(),
area(),
pos(),
"player",
];
const evilPlayerComponents = [
playerComponents, // or later when calling add(playerComponents, evilPlayerComponents)
color(0, 0, 0),
origin("left"),
];
add(evilPlayerComponents);
You can do add([[[[[body()]]]], pos()]) or any form of nesting, it should still work!
The RFC is fully implemented and typescript supports it.
I like this. Do you think we should generally advocate array-less add()? One thing is in addLevel() it uses array to define what each symbol means and calls add() later, wonder if we advocate array-less add() by default will it create more confusion when they're using addLevel()
Yeah I saw that. Not sure to be honest. I can see people doing '=': () => (sprit("poop"), solid()) which doesn't work.
My first thought for this was to have compose or join function and ditch arrays altogether i.e. '=': () => compose(sprit("poop"), solid()). But not sure how beginners would respond to it. It'd be good to do some user research here.
Another concern is if we should reserve the nested comps syntax. Kaboom (kinda) plans to moves to a node-tree structure from flat list where characters can have children who inherits transform attributes (position, scale, rotation), it'll be nice to use arrays to express this:
// the parent
add([
sprite("hero"),
pos(100, 100),
area(),
// a child
[
sprite("sword"),
pos(-10, 0),
],
// another child
[
sprite("pet"),
float(),
],
]);
but not sure yet.
(btw "solid poop" is a 👍 )
:hankey:
Interesting, haven't thought about that, would position be origined on the parent? I think mixing composition and inheritance API can be a trap and confusing. Feels like an API similar to follow makes more sense to me. Alternatively, nested add calls, like so
add([
sprite("hero"),
pos(100, 100),
area(),
// a child
add([
sprite("sword"),
pos(-10, 0),
]),
// another child
add([
sprite("pet"),
float(),
]),
]);
We can have an opaque Symbol on Characters to identify nested children added via add
Also thinking about
hero = add([
sprite("hero"),
pos(100, 100),
area(),
parent(), // gives addChild capabilities
]);
hero.addChild([ // same API as `add`
sprite("sword"),
pos(-10, 0),
]);
This gets rid of nesting altogether, which feels like it'll make the mental model easier.
Yep, or maybe
const sword = add(...);
const pet = add(...);
add([
sprite("hero",
children(sword, pet),
]);
I'll merge this to allow the syntax, but will think more if we want to start advocating this syntax over the existing one 🤔 .
also i think the blockly version of kaboom (which targets more beginners) will be hiding the array syntax for add() anyway