How do I avoid the "coding pyramid of doom"

  1. What do you want to achieve?
    I want to find a more elegant way to deal with instances that have parents that may or may not exist. Other languages like Swift or TypeScript have Optional Chaining which allows you to do something like this:
local childOfModel = game.Workspace.default?.part
--if game.Workspace.default exists, then it childOfModel = game.Workspace.default.part
--if game.Workspace.default does not exist, then childOfModel = nil
  1. What is the issue?
    The problem is that I keep on having to run into the Pyramid of Doom, which is really messy, and makes everything hard to read. Example of something that I have in my scripts very often:
local model = game.Workspace.template:Clone()
if model:IsA("Model") and model.Name ==  "default" then
    if model.child then
        if model.child.child2 then
            if model.child.child2.child3 then
                local attribute = model.child.child2.child3:GetAttribute("cost")
                if attribute then
                    return attribute
                else
                    model.child.child2.child3:SetAttribute("cost", 60)
                end
            end
        end
    end
end

Is there a more elegant solution than this? There isn’t necessarily a problem with the current solution of just using a bunch of nested if/elses to check if something is null, but it gets really annoying after a while.

  1. What solutions have you tried so far?
    I’ve basically tried to use roblox-ts for a week, which includes optional chaining as a feature. It’s been working great, but debugging is difficult because when ts is translated to Lua, it becomes much harder to read, especially in longer scripts.

Instead of doing that try putting it in a function and doing

if not model.child then return end
1 Like

Is it possible to do model:WaitForChild("Child1"):WaitForChild("Child2"):WaitForChild("Child3") that way if the child isn’t there it will wait, but after enough time if the child still isn’t there then it would return an error. If you don’t want an error to break the script wrapping it in a pcall should help.

So in the code you supplied you can do

local model = game.Workspace:WaitForChild("Template"):Clone()
local Attribute = model:WaitForChild("Child1") -- So on..
if Attribute:GetAttribute("Cost") then 
   return Attribute:GetAttribute("Cost") 
else
   Attribute:SetAttribute("Cost", 60)
end

It may be possible to condense some boolean expressions into variable declarations.

local child = model:FindFirstChild("child")
local child2 = child and child:FindFirstChild("child2")
local child3 = child2 and child2:FindFirstChild("child3")
if (model:IsA("Model") and model.Name == "default" and child3) then
-- ...
end

If either ‘child’ or ‘child2’ don’t exist, ‘child3’ will still resolve to ‘nil’.

If you want to take the risky way

if model:FindFirstChild("child3", true) then
    -- do things with it
end

FindFirstChild with the second parameter set to true will make it recursive, which looks in every descendant.

1 Like

That will still end up in the pyramid of doom

It won’t though, because you only need the one line.