Skip to content

Instantly share code, notes, and snippets.

@Anaminus
Created October 8, 2014 05:34
Show Gist options
  • Select an option

  • Save Anaminus/a22d928fc81f15097d5f to your computer and use it in GitHub Desktop.

Select an option

Save Anaminus/a22d928fc81f15097d5f to your computer and use it in GitHub Desktop.
Proposal to add function that returns internal value type

Proposal to add function that returns internal value type

ROBLOX provides many value types other than the Lua primitives, so it should be possible to distinguish these types beyond Lua userdata. Like the type function, a similar function which distinguishes these types should be implemented. Such a function could be used to enforce stricter typing of values, improving error detection and logging.

Function name

The name of the function could be some variation of "roblox" and "type". These should be enough to convey the purpose of the function. A short name is preferred, since the usage of the function is meant to be primitive. This proposal will use "rbxtype" to refer to the function, and for examples.

API

rbxtype(value) string

Receives any value, and returns the value's internal ROBLOX type as a string.

"Sub-typing"

rbxtype is meant to be used in the context of ROBLOX types. A value may have more type-like information related to it, but that does not exist as far as rbxtype is concerned. Once a basic type is determined by rbxtype, further information can be determined using whatever APIs are available.

The following subsections describe how certain types should be handled.

Lua types

Passing a Lua type (e.g. numbers, strings, tables) should return a single distinct type (e.g. rbxtype(12) == 'LuaValue'). Once the value has been determined as a Lua type, more information can be determined with the type function.

This is mostly for the sake of consistency. In the context of Lua scripts, Lua types are more primitive, so it would generally not be necessary to use rbxtype to verify a Lua type.

Instances

Any ROBLOX instance (e.g. BasePart, Script, Workspace) should return a single type (e.g. rbxtype(Instance.new('Part')) == 'Instance'). Once a type has been determined as an Instance, more information can be determined with the ClassName property or the IsA function. Example:

function VerifyClass(value, class)
	assert(type(class) == 'string')

	if rbxtype(value) ~= 'Instance' then
		error("argument must be an Instance", 2)
	end

	-- Value is an Instance; IsA can be used successfully.
	if not value:IsA(class) then
		error("argument must be a " .. class, 2)
	end
end

function ManipulatePart(part)
	VerifyClass(part, 'BasePart')

	-- Modify property inherited by BasePart class.
	-- The use of VerifyClass guarantees that the
	-- property will be indexed successfully.
	part.Position = Vector3.new(0,0,0)
end

Enums

To continue with the pattern, enums and enum items should each return a single value. For example:

  • rbxtype(Enum.NormalId) == 'Enum'
  • rbxtype(Enum.NormalId.Front) == 'EnumItem'
  • rbxtype(Enum) == 'EnumContainer'

Unfortunately, there isn't a complete API that can be used to get further information about them. EnumItems have a Name and Value property, but that's about it. Currently, more info can be gathered by parsing the results of a call to tostring. However, that's more of a hack than a real API. Here's a sub-proposal:

  • Add a function to enums that simply returns the name of the enum (e.g. Enum.NormalId:GetName() == 'NormalId'). It should be a function, opposed to a property, so that it does not conflict with the indexing of EnumItems (e.g. Enum.NormalId.Front).
  • Add a property to enum items that returns the associated enum (e.g. Enum.NormalId.Front.Enum == Enum.NormalId). Returning the direct value is preferred, rather than just the name, so that it is not necessary to get the actual enum by indexing the Enum container.

Example:

function VerifyEnum(value, enum)
	assert(rbxtype(enum) == 'Enum')

	if rbxtype(value) ~= 'EnumItem' then
		error("argument must be an EnumItem", 2)
	end

	if value.Enum ~= enum then
		error("argument must be a " .. enum:GetName(), 2)
	end
end

function SetFace(surfaceGui, face)
	VerifyClass(surfaceGui, 'SurfaceGui')
	VerifyEnum(face, Enum.NormalId)

	surfaceGui.Face = face
end

For completeness, the Enum container should also return some kind of type.

Void / no value

Unlike the type function, which throws an error when no value is passed, it might be convenient to return "void" when no value is passed to rbxtype. However, it would be less consistent with type. The choice would probably depend on how no-value is handled internally.

Unknown types

If the type of a value cannot be determined, it should return a distinct value indicating such (e.g. "UnknownType").

Examples

rbxtype()                                == "void"

rbxtype(nil)                             == "LuaType"
rbxtype(false)                           == "LuaType"
rbxtype(12)                              == "LuaType"
rbxtype("hello")                         == "LuaType"
rbxtype({1,2,3})                         == "LuaType"
rbxtype(function()end)                   == "LuaType"
rbxtype(coroutine.create(function()end)) == "LuaType"
rbxtype(newproxy())                      == "LuaType"

rbxtype(Axes.new())                      == "Axes"
rbxtype(BrickColor.new())                == "BrickColor"
rbxtype(CFrame.new())                    == "CFrame"
rbxtype(Color3.new())                    == "Color3"
rbxtype(Faces.new())                     == "Faces"
rbxtype(Ray.new())                       == "Ray"
rbxtype(Region3.new())                   == "Region3"
rbxtype(Region3int16.new())              == "Region3int16"
rbxtype(Vector2.new())                   == "Vector2"
rbxtype(Vector2int16.new())              == "Vector2int16"
rbxtype(Vector3.new())                   == "Vector3"
rbxtype(Vector3int16.new())              == "Vector3int16"
rbxtype(UDim.new())                      == "UDim"
rbxtype(UDim2.new())                     == "UDim2"

rbxtype(Game)                            == "Instance"
rbxtype(Workspace)                       == "Instance"
rbxtype(Instance.new('Part'))            == "Instance"
rbxtype(Instance.new('Script'))          == "Instance"

rbxtype(Enum.NormalId)                   == "Enum"
rbxtype(Enum.NormalId.Front)             == "EnumItem"
rbxtype(Enum)                            == "EnumContainer"

rbxtype(Game.Changed)                    == "RBXScriptSignal"
rbxtype(Game.Changed:connect())          == "RBXScriptConnection"

rbxtype(UnknownValueType)                == "UnknownType"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment