Menu
Coddy logo textTech

Duck Typing

Part of the Object Oriented Programming section of Coddy's Lua journey — lesson 39 of 70.

Lua doesn't care about an object's class—it only cares about what the object can do. This concept is called duck typing: "If it walks like a duck and quacks like a duck, it's a duck." In programming terms, if an object has the method you're calling, it works.

This means completely unrelated classes can be used together, as long as they share the same method name:

local Circle = {}
Circle.__index = Circle
function Circle:new() 
    local obj = {}
    setmetatable(obj, Circle)
    return obj
end
function Circle:draw()
    print("Drawing a circle")
end

local Square = {}
Square.__index = Square
function Square:new()
    local obj = {}
    setmetatable(obj, Square)
    return obj
end
function Square:draw()
    print("Drawing a square")
end

These classes have no inheritance relationship—they're completely independent. Yet we can treat them identically:

local shapes = {Circle:new(), Square:new(), Circle:new()}

for _, shape in ipairs(shapes) do
    shape:draw()
end
-- Output:
-- Drawing a circle
-- Drawing a square
-- Drawing a circle

The loop doesn't check what type each object is. It simply calls :draw() and trusts that the method exists. This flexibility is powerful—you can add new shape types without modifying the loop code at all. As long as the new class implements :draw(), it just works.

challenge icon

Challenge

Easy

Let's demonstrate the power of duck typing by building a notification system with completely unrelated classes that can all be processed the same way!

You'll create three independent notification classes—none of them inherit from each other—but they all implement a :send() method. This means you can loop through a mixed collection and call :send() on each one without caring about its actual type.

You'll organize your code across four files:

  • Email.lua: A class with a :new(recipient) constructor that stores who the email goes to. Its :send() method should print Emailing {recipient}.
  • SMS.lua: A completely separate class with a :new(phoneNumber) constructor. Its :send() method should print Texting {phoneNumber}.
  • PushNotification.lua: Another independent class with a :new(deviceId) constructor. Its :send() method should print Pushing to {deviceId}.
  • main.lua: Bring everything together! Read three inputs, create one instance of each notification type, put them all in a single table, then loop through and call :send() on each one.

You will receive three inputs:

  1. An email recipient (e.g., alice@example.com)
  2. A phone number (e.g., 555-1234)
  3. A device ID (e.g., device_99)

In your main file, create all three notification objects, store them in a table, and iterate through the table calling :send() on each. The order should be: Email first, then SMS, then PushNotification.

For example, if the inputs are bob@mail.com, 555-9876, and phone_42, the output should be:

Emailing bob@mail.com
Texting 555-9876
Pushing to phone_42

Notice how your loop doesn't need to check what type each object is—it just trusts that every object in the collection has a :send() method. That's duck typing in action! If it can :send(), it's a notification.

Try it yourself

-- main.lua
-- Bring all notification types together and demonstrate duck typing

-- Import the notification classes
local Email = require('Email')
local SMS = require('SMS')
local PushNotification = require('PushNotification')

-- Read inputs
local recipient = io.read()
local phoneNumber = io.read()
local deviceId = io.read()

-- TODO: Create one instance of each notification type

-- TODO: Put all notifications in a single table (Email first, then SMS, then PushNotification)

-- TODO: Loop through the table and call :send() on each notification
-- This demonstrates duck typing - we don't check the type, we just call :send()
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming