NumberRange, NumberSequence, and ColorSequence properties should accept numbers & Color3s

These properties are really annoying to use, because nearly every time when I want to set one of the properties to a single constant value, my first instinct will be to just set it to the number or Color3 directly:

ParticleEmitter.Speed = 5

But this will of course throw an error since in this case ParticleEmitter.Speed is actually a NumberRange.

It’s especially painful when the properties are not known by the typechecker (e.g. you are defining a table of props), in which case the only way to fix these mistakes is to repeatedly run the code and fix them one at a time until they are all gone.

It would be really great if the engine would detect that number or Color3 is just a special case of a NumberRange, NumberSequence, or ColorSequence value and auto-convert it where possible.

These data type names are also quite long so they add a lot of extra verbosity to our code when we just want to define a tiny little constant value.

9 Likes

I think a better option would simply be an option to create static sequences from the constructors themselves (to prevent assignment bloat). Something like:

ColorSequence.fromColor(color3)

and:

NumberSequence.fromNumber(number)

Although slightly longer, it’s still much easier to write and doesn’t require extra checks when assigning properties, which can become costly when done frequently.

1 Like

Actually, you can already do NumberSequence.new(number) and likewise for all the other data types. The issue is that it’s hard to remember and annoying to type the full datatype constructer, and hard to find the mistakes (without the help of the typechecker) when it’s forgotten. It’s just a UX problem, a timewaster, that I run into very frequently.

6 Likes

I highly doubt this is even remotely costly when there are already examples present of other properties exhibiting these sorts of convenience behaviors (try to set something like a part’s Name to a number and it automatically converts to a string).

2 Likes

Oh wow, I didn’t even know that existed.

However, my point still stands with the performance aspect. It doesn’t make sense to have extra checks before assigning a property unless it’s totally necessary. Adding this feature would drastically slow down code that assign these properties frequently. It’s already trivial to type in NumberSequence.new(number). As an added consequence, having two datatypes assign to one can cause some level of confusion, especially for newer developers.

The example provided is a language niche–and a pretty bad one at that. Luau only has this feature to retain backwards compatibility. Lua allows for some very weird combinations of strings and numbers, which I consider to be horrendous design for a language:

print("5" + 5) --> 10
print("5" + "5") --> 10
print("5" .. 5) --> 55

The only reason you can assign numbers to string entries lies entirely on the existence this feature. You can’t find this anywhere else.

1 Like

The example provided is specifically how property setting for string properties functions and has nothing to do with the language. All you did was provide examples of how Lua treats maths & concatenation for numbers & strings and would not just magically apply to the interface Roblox has created for interacting with the datamodel without them explicitly introducing this behaviour.

3 Likes

Sorry, when I said “a pretty bad one at that”, I was talking about the language niche, not your example. I was explaining why Roblox allows setting number values to string properties.

The automatic type coercion has been scrutinized by many developers alike. It is not a great feature. Not only can it be costly in terms of performance, but it sometimes causes unintended behavior, as well as incentivizing bad code.

1 Like

A better example is how Roblox allows you to assign number/strings to Enum properties, e.g Part.Material = "Neon" works

Theres also a case where roblox allows you to use 1 and 0 for boolean properties (e,g Part.CanCollide = 1 works)

Also, the opposite case of what @TheGrimDeathZombie said also works; you can set number properties using a string (e.g Part.Transparency = "0.5" works)

None of these coercions cause any major performance cost, and it’s not like you’re setting ParticleEmitter.Speed a million times a frame (and at that point most of your performance cost is coming from crossing the Lua->C++ bridge, not type coercions :V )

Also none of these incentivize bad code, they’re still very understandable, just easier to use and type.

In general I don’t see a reason for this feature to not get added when many cases of type coercion for properties already exist

5 Likes

Nobody really knows the cost of this except Roblox staff :stuck_out_tongue: But the naive theoretical minimum cost is an extra compare and jump instruction which is fairly small. It may even be less than that if the runtime type checking is already implemented as a table lookup.

I do agree with this, and it’s icky to be mixing datatypes, but I think the tradeoff for the improved UX is worth it in this case. I believe the mixing here is less icky than the legacy string ← → number and bool ← → number conversions because the data types here are meaningfully related. NumberSequence is essentially a superset of number, and ColorSequence is a superset of Color3.

1 Like

I’m Honestly Shocked They Haven’t Done this yet, it trips me up very often

1 Like

Again, all of this is legacy behavior. Just because legacy allows it does not mean that we should follow its standards. Allowing the assignment of other datatypes, even if they’re indeed similar, is widely agreed as bad practice, and is one of the biggest performance pitfalls of Lua in general.

Consider this post by Roblox staff:

There is legacy behavior that does exist that “support” this feature request. But clearly, apart from being backward-compatible, they are not eager to implement features like these.


I cannot speak for Enum values as I don’t know how they work internally. However, it can still be considered legacy behavior.


Again, this is legacy Lua behavior. You can set a boolean value to any truthy value, and it will be evaluated to be true. This only works due to a byproduct of Lua, not because it was intentionally designed.


Again, just a byproduct of legacy Lua behavior.


Allowing multiple datatype assignments into a property is widely regarded as bad practice (in most situations). If you write your code correctly, you should not be mixing datatypes within table indices, class properties, or similar structures.


Dictionary lookups may seem quick in the grand scheme of things, but they can take O(n^2) time in the worst case scenario (which, if you don’t know already, is really slow). There may be a minimum cost, but performance-critical code, such as animation code that frequently interpolates ColorSequences or NumberSequences, will see minor performance issues. The UX improvement is minimal, but the performance impact can drastically outweigh any benefits.

Even if they’re related, it doesn’t mean that it’s worth implementing. There are many cases where you do not use the rotation matrix inside CFrames, but Roblox will not provide Vector3 assignments to CFrame properties, because that doesn’t make sense, even if it happens often. Developers are then forced to assign with CFrame.new(Vector3).

When I said “table lookup” I was not referring to dictionaries, I was talking about using variable input as an integer index into an array. It’s an alternative to using if statements / switch statements. i.e. instead of using a comparion & jump it uses a memory operation. In any case I don’t think it’s productive to keep arguing about performance here.

That’s a good point, however CFrame data types are usually indicated in the property name by various conventions (Transform, CFrame, C0/C1) which makes mistakes rarer, whereas for things like ParticleEmitters, Trails, and UIGradients the property name gives no hints about the data type and there are usually lots of different properties under the class which need to be “supersetted” which makes it easy to mess up and lose track. I do get bitten by the CFrame mistake sometimes anyway, but I don’t mind as much because it’s less frequent .

2 Likes

Many of the type coercions exist only for backwards compatibility reasons and we are strongly discouraging using them.

In fact all of your examples give script analysis warnings.
We are also introducing more runtime warnings so that developers stop relying on many forms of type coercions and make their code better.

Relying on type coercions absolutely does incentive bad code and cause developers to unknowingly introduce bugs to their code that are hard to track down later.

For these reasons we try hard to avoid introducing new cases of type mismatches being allowed and do not plan to implement this feature request.

2 Likes