How to use metamethod __call with colon as a value of another Table

I want to be able to call the method __call using colon while it as a value of a table

It seems like there is a problem with using the colon and the dot operator because all of them act differently. When using the colon it prints out itself instead of printing the text which was indicated by the parameter.

I have tried addied self as one of it parameter and removing it and adding a temporary variable when activating the function but still doesn’t work

Here is a sample lua code I had wrote:

--!strict
local Meta = {}

function Meta:Foo()
	print("Foo")
end

local Object = {}
Object.String = "Hello World!"

local function Assign()
	return setmetatable(Object, {
		__index = Meta,

		__newindex = function(self, Key, Value)
			error(`Attempted to change or create {Key} with {Value}`)
		end,

		__call = function(self, Text: string)
			print(Text)
		end,
	}) :: (typeof(Object) & typeof(Meta)) & (Text: string) -> nil
end

local MetaTable = Assign()

local Table = { ["Object"] = MetaTable }

Table.Object("Hello World!")

Table:Object("This is a colon") --Although shows a warning with Luau type-checking it still works but instead of printing the Text it prints Table, the parent of it.

Table.Object:Foo()

Found a kind of working solution if you doesnt intent to access table value. I didn’t thought that I would need to place it outside of the scope so that it will create an annoying autofill for the __call method

--!strict
local Meta = {}

function Meta:Foo()
	print("Foo")
end

--Created the __call method with colon
function Meta:__call(Table, Text: string)
	print(Text)
end

local Object = {}
Object.String = "Hello World!"

local function Assign()
	return setmetatable(Object,{
		__index = Meta,

		__newindex = function(self, Key, Value)
			error(`Attempted to change or create {Key} with {Value}`)
		end,
		
		__call = Meta.__call --Redirect __call to Meta.__call
	}) :: (typeof(Object) & typeof(Meta)) & (self:any,Text: string) -> nil
end

local MetaTable = Assign()

local Table = { ["Object"] = MetaTable }

Table:Object(Table.Object.String) -- Now working with autofill and the __call method

Just know how to get rid of the autofill

--!strict
local Meta = {}

function Meta:Foo()
	print("Foo")
end

--Created the __call method with colon
local Call = {}
function Call :__call(Table, Text: string)
	print(Text)
end

local Object = {}
Object.String = "Hello World!"

local function Assign()
	return setmetatable(Object,{
		__index = Meta,

		__newindex = function(self, Key, Value)
			error(`Attempted to change or create {Key} with {Value}`)
		end,
		
		__call = Call.__call --Changed to separate function Work with self
	}) :: (typeof(Object) & typeof(Meta)) & (self:any,Text: string) -> nil
end

local MetaTable = Assign()

local Table = { ["Object"] = MetaTable }

Table:Object(Table.Object.String)
Table.Object. --hide the __call function

Turns out… It is more complicated than that. The __call method is trying to access the Table storing it instead of the table it has been designated too by setmetatable. It works with a simple task that doesn’t involve going into the table to get the value and it turns out I need the table value. I have tried wrapping the table, failed miserably through errors and errors. I tried putting the Table parameter in the function Call:__call doesn’t work, same result table thinking itself as its parent. I tried to think why this happened, maybe because of the colon? or the dot? The dot works fine then why not the colon? Shouldn’t it fill itself in instead or take its parent as the self? Complicated things keep happening. I am stuck for now. It works for a simple task that doesn’t need to access the table value but turns out I need to in order to make my code work. I was able to solve the autofill but now the Table parameter is just acting weird.

After some more testing it seem to keep getting weird. If the __call method is from a table with __call function that is define by colon it won’t print out it self. But when doing __call method in the setmetatable which is defining the __call method as a function of itself it will print out it Table and the Text. Here an example code of the __call method as a function in setmetatable problem:

local Meta = {}

function Meta:Foo()
	print("Foo")
end

local Object = {}
Object.String = "Hello World!"

local function Assign()
	return setmetatable(Object,{
		__index = Meta,

		__newindex = function(self, Key, Value)
			error(`Attempted to change or create {Key} with {Value}`)
		end,

		__call = function(T, Text)
			print(Object)
			print(T)
			print(Text)
		end,
	})
end

local MetaTable = Assign()

local Table = { ["Object"] = MetaTable }

print("Colon")

Table:Object("Nothing matter in here","Even the second Parameter") --Print Object, Self (Object), Table

print("Dot")

Table.Object("Dot Called") --Print Object, Self (Object), "Dot Called"

I can think of putting the Object in the function Call:__call but it wouldn’t work with oop I can either return the function through Call:__call although it keep throwing me error saying attempt to call a table value

the reason why it prints itself when using colon is because when you use colon to call a method, the first argument of that method will be set to the table you’re calling the method on. For example, if I have an object with a function called Destroy, then it would be easier to do Object:Destroy() than Object.Destroy(Object). The destroy function would look like this: function ObjectMeta:Destroy() self.GameInstance:Destroy() end. This has to do with the keyword self. By the way, it’s important that I also include a colon at the function definement: “function ObjectMeta:Destroy()” The reason I add a colon there is to set self to the first argument and after that you can neglect the first argument in the parameter parantheses because it’s already gotten rid of. That’s why you’re getting different results depending on if you use a colon or a dot. This video explains it amazingly: What is the self keyword in Lua? - Roblox Studio - YouTube

1 Like

Thank you for your reply and explaining to me more about self and also your contribution to this post. I was able to solve my problem somehow by a magical programming accident and when I looked into it more I noticed I was basically replacing the first parameter of the __call as self or Table and using it as I thought it would print the table being called instead of the table holding the value. So I changed it (In reality I just forgot to use the first parameter and I acted crazy when it worked) and used self only and ignored the first parameter completely.

local Function = {}

function Function:Foo()
	print("foo")
end

local Table = {}

Table.Value = 1
Table.Text = "Good luck."

local Meta = {}
Meta.__index = Function

Meta.__newindex = function(self, K, V)
	error(`{K}, {V}`)
end

function Meta:__call(_, Text)
	print(self) -- Print ["t"] (aka Table)
	print(self.Value) -- Print ["t"].Value (aka Table.Value)
	if getmetatable(self) == Meta then
		print(getmetatable(self)) --Meta
		print(Text) --"Hi"
	end
end

setmetatable(Table, Meta)

local ParentTable = {["t"] = Table}
ParentTable:t("Hi")
--ParentTable.t("Hi")
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.