Type-Checking Idiom in Lua

Today I want to share a simple Lua idiom for assigning a value to a variable only if that value has a specific type.

Our Example Task

We are going to create a User table which:

  1. Has a name property.

  2. Has a setName() method which will accept an argument and assign that to the name property but only if the argument is a string.

First Implementation, Example, and Explanation

local User = {}

function User:setName(name)
    self.name = type(name) == "string" and name
end

We can use this code like so:

User:setName("Eric")
print(User.name)        -- Prints string "Eric"

User:setName(123456)
print(User.name)        -- Prints boolean false

Why does the second example print a boolean? The answer lies in the implementation of setName(). Let’s look closely at what we assign to self.name:

type(name) == "string" and name

We use the value of this boolean expression. If type(name) == "string" is true then we check to see if name is non-nil. But in reality we already know that it’s not nil, otherwise the first part of the expression could not be true. So we are taking advantaging of the fact that when Lua evaluations this boolean and expression it will give us the last non-nil, ‘truthy’ value in the expression, assuming nothing evaluates to nil of course. This means setName() behaves as if we had written it this way:

function User:setName(name)
    if type(name) == "string" then
        self.name = name
    else
        self.name = false
    end
end

This shows why in the original example name had the boolean value false after we had called User:setName(123456). The value of the and expression will either be a string or false. But what if we want a different default value?

Second Implementation: Controlling Default Values

We know that the expression in setName() will be boolean false when we call the method with anything that’s not a string. We want to change the expression so that it evaluates to either the string argument we pass to the method or a default value. This line of thought leads us directly to this modification:

function User:setName(name)
    self.name = (type(name) == "string" and name) or "Unnamed"
end

The parentheses in the boolean expression are optional and only there to help separate our modification from the original implementation. Now we are assigning self.name the value of a boolean or expression. The left side is the and we started with, so we already know how it will either be the name paramater or boolean false depending on the parameter’s type. The right side of the or expression is a string with our default value. Lua is a language which considers many non-boolean values to be true in a boolean expression, and our default "Unnamed" string is an example of such a value.

Using this version of the method we get the following results from our previous tests:

User:setName("Eric")
print(User.name)        -- Prints string "Eric"

User:setName(123456)
print(User.name)        -- Prints string "Unnamed"

Conclusion

To summarize, we used two variations of the idiom:

  1. type(variable) == "string" and variable

  2. type(variable) == "string" and variable or "Default"

We used strings for the example but could just have easily used numbers or tables. If we are using tables and metatables for object-oriented programming then we could introduce another and using getmetatable() to differentiate between types of objects. But in my personal experience going down that path can quickly produce code that is less readable than the equivalent using if-else statements.

I hope you find this idiom useful, as you may see it in code even if you never use it yourself. And please share any Lua idioms you like to use by leaving a comment below!

Advertisements

2 thoughts on “Type-Checking Idiom in Lua

  1. My worry is that if someone passes a non-string into the setName function, they have probably lost track of what is going on in their program. In that case, it would be better to raise an error so that the programmer is notified.

  2. a neat extension of this idiom is:
    self.prop = type(prop) == ‘string’ and prop or defaultvalue

    this maintains the behavior above, but also allows you to default to a generic (but still valid!) value.
    also, adding parens around the type check improves readability by making it obvious that the type check is a single value:
    self.prop = (type(prop) == ‘string’) and prop or defaultvalue

Add Your Thoughts

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s