Skip to content

phantomBindingPatterns idea #645

@hazzard993

Description

@hazzard993

Some Lua libraries (especially LÖVE 2D) can have many parameters for functions. This can result in situations where you read many lines of code that look like this:

love.graphics.draw(playerSprite, 0, 0, math.rad(90), 1, 1, 0, 0, 16, 16)

Here's what the function call would be likely to look like if it came from JS since JS devs prefer object literals over many parameters, unlike Lua devs who avoid creating tables and function expressions especially for code that is executed multiple times:

love.graphics.draw(playerSprite, {
    positionX: 0,
    positionY: 0,
    orientation: math.rad(90),
    scaleX: 1,
    scaleY: 1,
    offsetX: 0,
    offsetY: 0,
    shearX: 16,
    shearY: 16,
});

Reads a lot better with an object literal describing what each parameter is called.

Another possible call:

love.graphics.draw(playerSprite, { offsetX: 16 });

Since every parameter is optional, only offsetX is used.

So the idea of this directive is to allow functions like this:

declare function draw(drawable: Drawable, x?: number, y?: number, r?: number, ...): void;

To also be declared as:

/** @phantomBindingPattern */
declare function draw(drawable: Drawable, {
    positionX,
    positionY,
    orientation,
    scaleX,
    scaleY,
    offsetX,
    offsetY,
    shearX,
    shearY
}: {
    positionX?: number,
    positionY?: number,
    orientation?: number,
    scaleX?: number,
    scaleY?: number,
    offsetX?: number,
    offsetY?: number,
    shearX?: number,
    shearY?: number,
}): void;

Which would cause this to happen:

draw(playerSprite, { positionX: 0, positionY: 0 });
// draw(playerSprite, 0, 0)

draw(playerSprite, { positionY: 0 });
// draw(playerSprite, nil, 0)

draw(playerSprite, { offsetX: 16 });
// draw(playerSprite, nil, nil, nil, nil, nil, 16)

Which means no table literals are generated and users can take advantage of the improved code readability.

The order of the function declarations' binding pattern elements matter.

declare function print({x, y}): void;
declare function printReverse({y, x}): void;
print({x: 0, y: 1});
// print(0, 1)
printReverse({x: 0, y: 1});
// printReverse(1, 0)

Nested binding patterns are also possible which stick to the same ordering rule

declare function print({x: {x}, y}): void;
print({ x: { x: 0 }, y: 1 });
// print(0, 1)

This should also be usable on any function implementation which can help reduce the table literals created.

/** @phantomBindingPattern */
function test({ x, y }) {
    console.log(x, y);
}

This destructure would also cause non-object-literals to be destructed appropriately with their properties being assigned to the appropriate positions within a function's parameters. If those properties exist on the object.

const position = { positionX: 0, positionY: 0 };
draw(playerSprite, position);
// draw(playerSprite, position.positionX, position.positionY)

For cases like these it is possible to compute that positionX will be 5 at the end of computing this destructure. However doing things like this could be supported later on.

draw(playerSprite, {
    positionX: 0,
    positionY: 0,
    ...{ positionX: 5 },
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions