Overload: Instance.new(string className, table properties)

Pretty simple feature. Right now we have to either include the RbxUtility library and use the Create function, or write our own implementation if we want to set all the properties of a part based on a table.

I propose that Instance.new have an overload where the second argument is a table of properties to be set on the instance.

local firePart = Instance.new("Part", { Name = "FirePart"; Anchored = true; BrickColor = BrickColor.new("Really red"); Parent = Workspace }) print(firePart.Name) --> FirePart

instead of:

[code]local firePart = Instance.new(“Part”)
firePart.Anchored = true;
firePart.BrickColor = BrickColor.new(“Really red”)
firePart.Parent = Workspace

print(part.Name) → FirePart
[/code]

It helps with readability and if you change the name of a variable when instantiating it, you don’t have to go through and change the variable name on each line.

[strike]Don’t forget the optional last property for parent

Instance.new(“Part”, {
Size=Vector3.new(1,1,1);
},
workspace)[/strike]

[strike]Redacted. Instead, if the second parameter is a table, function like in the OP, otherwise if it’s an object, set the new instance’s parent to that. This would prevent old scripts that use a second parameter to set the parent from breaking.[/strike]

Lrn2Vocab echo

[quote] Don’t forget the optional last property for parent

Instance.new(“Part”, {
Size=Vector3.new(1,1,1);
},
workspace) [/quote]

I disagree with this. Parent can be set within the property table so having it as an extra argument leads to less readability.

[strike][quote] Don’t forget the optional last property for parent

Instance.new(“Part”, {
Size=Vector3.new(1,1,1);
},
workspace) [/quote][/strike]

Redacted. Instead, if the second parameter is a table, function like in the OP, otherwise if it’s an object, set the new instance’s parent to that.

[quote]
Redacted. Instead, if the second parameter is a table, function like in the OP, otherwise if it’s an object, set the new instance’s parent to that. This would prevent old scripts that use a second parameter to set the parent from breaking. [/quote]
That’s what an overload is :slight_smile:

Anything so that I don’t use up 10 lines to reformat the stupid unanchored, non-SmoothPlastic, studs on top and bottom, Elasticity 0.3, BRICK FORMFACTOR instanced parts.

Instead I could just copy paste 1 line from wherever, and have that be my “perfect part” code.

[quote] Anything so that I don’t use up 10 lines to reformat the stupid unanchored, non-SmoothPlastic, studs on top and bottom, Elasticity 0.3, BRICK FORMFACTOR instanced parts.

Instead I could just copy paste 1 line from wherever, and have that be my “perfect part” code. [/quote]
You can just create a base part, and clone it every time…

Unfortunately that sort of syntax (however much I like it in my own code) is kind of unacceptable to put into the public API for the following reason:

local part = Instance.new('Part', {
    Name = 'Block';
    Size = Vector3.new(0.1, 0.1, 0.1);
    FormFactor = 'Custom';
})

Is that code correct? Unfortunately, the answer is not at all obvious without actually running the code, even to the most experienced users. I don’t believe that there’s any reasonable solution to that problem, which is why I haven’t tried to get such a construct included in the API.

[quote] Unfortunately that sort of syntax (however much I like it in my own code) is kind of unacceptable to put into the public API for the following reason:

local part = Instance.new('Part', {
    Name = 'Block';
    Size = Vector3.new(0.1, 0.1, 0.1);
    FormFactor = 'Custom';
})

Is that code correct? Unfortunately, the answer is not at all obvious without actually running the code, even to the most experienced users. I don’t believe that there’s any reasonable solution to that problem, which is why I haven’t tried to get such a construct included in the API. [/quote]

wot – how is it not obvious? The Size will be (1,1.2,1) because the default FormFactor is Brick(why is this a thing) – Custom needs to go before the Size, assuming ROBLOX goes through the table in order.

“assuming ROBLOX goes through the table in order.”

Thank you, case and point exactly: Even you don’t realize what’s going on, even after I pointed out that something was up. The hash part of a table has no “order”. Yes, the order is deterministic, but the order that you initialize the property map in will not be the same order that an iteration of the property map sees.

What order the properties will actually show up in upon iteration depends on several factors, such as the version of Lua, the order the operations were done on it, and even the size of the property map!. That means that whether the part will be <1,1.2,1> or <0.2,0.2,0.2> will actually depend on how many other properties you include in the table you’re passing!

EG:

> a = {Name = 'Block'; Size = 5; FormFactor = 'Custom'}
> for k, v in pairs(a) do print(k) end
FormFactor
Name           -----> Name is AFTER FormFactor
Size
> a = {a=5; b = 7; c = 7; d = 7; e = 7;  f = 7; Name = 'Block'; Size = 5; FormFactor = 'Custom'}
> for k, v in pairs(a) do print(k) end
a
c
b
e
d
f
Name           -------> Name is BEFORE FormFactor
FormFactor
Size

How about adding a __call metamethod to instance objects with the first argument as a table of properties. The metamethod will return the object itself again.

__call = function(self, properties) -- set properties return self end

Usage when creating new instances:

local part = Instance.new("Part",workspace) { Name = "Test", Anchored = true, Transparency = 0.5, CanCollided = false, Size = Vector3.new(10,10,10) }

Cases where you need to set a property before the other: (not the most efficient solution)

local part = Instance.new("Part",workspace) {
	Name = "Bomb",
	FormFactor = "Custom"
} {
	Anchored = true,
	CanCollided = false,
	Transparency = 0.5,
	BrickColor = BrickColor.new("Really blue")
	Size = Vector3.new(10,10,10)
}

Set properties of existing objects:

-- assuming 'FireBall' is a part instance
FireBall {
	Size = Vector3.new(100,100,100),
	Velocity = Vector3.new(50,50,100)
}

“Cases where you need to set a property before the other: (not the most efficient solution)”

Yes, that would work.

However, I think that it’s way too big of a “gotcha” to be worth it. The fact that I don’t think anyone other than maybe a dozen people on the whole of Roblox would have actually spotted that issue before they got bitten by it is the problem.

When you’re designing an API you have to think about not only how good it is to use once you know how to correctly use it, but how easy it is to incorrectly use it.

[quote] “assuming ROBLOX goes through the table in order.”
Thank you, case and point exactly: Even you don’t realize what’s going on, even after I pointed out that something was up. The hash part of a table has no “order”. Yes, the order is deterministic, but the order that you initialize the property map in will not be the same order that an iteration of the property map sees.
[/quote]
Is this caused by the iterator function or the way that Lua stores key/value pairs in tables?

“or the way that Lua stores key/value pairs in tables?”

The non-integer and sparse keys are stored in a hash-table. Hash tables associate keys with values but they don’t store the ordering information in any way. The ordering information is lost as soon as the table is constructed.

Sorry for necro, but couldn’t ROBLOX just do this in their code?

function Instance.new(classname, secondParameter)
    if type(secondParameter) == "table" then
        if className:IsA("BasePart") then
            local instance = new instance

            if secondParameter["FormFactor"] then
                instance.FormFactor = secondParameter["FormFactor"]
            end

            for property,value in pairs(secondParameter) do
                instance[property] = value
            end
        end
    else
        --do stuff as it is now
    end
end

Since there aren’t many "gotcha"s in which the order of property setting matters (FormFactor/Shape + Size is the only one I know exists), ROBLOX could just add a quick check to see if FormFactor was in the code and if it was, set that first.

You can use this in the meantime