Skip to main content

Brio

Brios wrap a value (or tuple of values) and are used to convey the lifetime of that object. The brio is better than a maid, by providing the following constraints:

  • Can be in 2 states, dead or alive.
  • While alive, can retrieve values.
  • While dead, retrieving values is forbidden.
  • Died will fire once upon death.

Brios encapsulate the "lifetime" of a valid resource. Unlike a maid, they

  • Can only die once, ensuring duplicate calls never occur.
  • Have less memory leaks. Memory leaks in maids can occur when use of the maid occurs after the cleanup of the maid has occured, in certain race conditions.
  • Cannot be reentered, i.e. cannot retrieve values after death.
info

Calling brio:Destroy() or brio:Kill() after death does nothing. Brios cannot be resurrected.

Brios are useful for downstream events where you want to emit a resource. Typically brios should be killed when their source is killed. Brios are intended to be merged with downstream brios so create a chain of reliable resources.

local brio = Brio.new("a", "b")
print(brio:GetValue()) --> a b
print(brio:IsDead()) --> false

brio:GetDiedSignal():Connect(function()
	print("Hello from signal!")
end)
brio:ToMaid():GiveTask(function()
	print("Hello from maid cleanup!")
end)
brio:Kill()
--> Hello from signal!
--> Hello from maid cleanup!

print(brio:IsDead()) --> true
print(brio:GetValue()) --> ERROR: Brio is dead

Design philosophy

Brios are designed to solve this issue where we emit an object with a lifetime associated with it from an Observable stream. This resource is only valid for some amount of time (for example, while the object is in the Roblox data model).

In order to know how long we can keep this object/use it, we wrap the object with a Brio, which denotes the lifetime of the object.

Modeling this with pure observables is very tricky because the subscriber will have to also monitor/emit a similar object with less clear conventions. For example an observable that emits the object, and then nil on death.

Properties

DEAD

Brio.DEAD: Brio

An already dead brio which may be used for identity purposes.

print(Brio.DEAD:IsDead()) --> true

Functions

isBrio

Brio.isBrio(valueany) → boolean

Returns whether a value is a Brio.

print(Brio.isBrio("yolo")) --> false

new

Brio.new(
...any--

Brio values

) → Brio

Constructs a new Brio.

local brio = Brio.new("a", "b")
print(brio:GetValue()) --> a b

delayed

since 3.6.0
</>
Brio.delayed(
timenumber,
...any--

Brio values

) → Brio

Constructs a new brio that will cleanup afer the set amount of time

GetDiedSignal

Brio:GetDiedSignal() → Signal

Gets a signal that will fire when the Brio dies. If the brio is already dead calling this method will error.

info

Calling this while the brio is already dead will throw a error.

local brio = Brio.new("a", "b")
brio:GetDiedSignal():Connect(function()
	print("Brio died")
end)

brio:Kill() --> Brio died
brio:Kill() -- no output

IsDead

Brio:IsDead() → boolean

Returns true is the brio is dead.

local brio = Brio.new("a", "b")
print(brio:IsDead()) --> false

brio:Kill()

print(brio:IsDead()) --> true

ErrorIfDead

Brio:ErrorIfDead() → ()

Throws an error if the Brio is dead.

brio.DEAD:ErrorIfDead() --> ERROR: [Brio.ErrorIfDead] - Brio is dead

ToMaid

Brio:ToMaid() → Maid

Constructs a new Maid which will clean up when the brio dies. Will error if the Brio is dead.

info

Calling this while the brio is already dead will throw a error.

local brio = Brio.new("a")
local maid = brio:ToMaid()
maid:GiveTask(function()
	print("Cleaning up!")
end)
brio:Kill() --> Cleaning up!

GetValue

Brio:GetValue() → any

If the brio is not dead, will return the values unpacked from the brio.

info

Calling this while the brio is already dead will throw a error. Values should not be used past the lifetime of the brio, and can be considered invalid.

local brio = Brio.new("a", 1, 2)
print(brio:GetValue()) --> "a" 1 2
brio:Kill()

print(brio:GetValue()) --> ERROR: Brio is dead

GetPackedValues

since 3.6.0
</>
Brio:GetPackedValues() → {
nnumber,
...T
}

Returns the packed values from table.pack() format

Destroy

Brio:Destroy() → ()

Kills the Brio.

info

You can call this multiple times and it will not error if the brio is dead.

local brio = Brio.new("hi")
print(brio:GetValue()) --> "hi"
brio:Kill()

print(brio:GetValue()) --> ERROR: Brio is dead

Kill

Brio:Kill() → ()

Alias for Destroy.

Show raw api
{
    "functions": [
        {
            "name": "isBrio",
            "desc": "Returns whether a value is a Brio.\n\n```lua\nprint(Brio.isBrio(\"yolo\")) --> false\n```",
            "params": [
                {
                    "name": "value",
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 77,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "new",
            "desc": "Constructs a new Brio.\n\n```lua\nlocal brio = Brio.new(\"a\", \"b\")\nprint(brio:GetValue()) --> a b\n```",
            "params": [
                {
                    "name": "...",
                    "desc": "Brio values",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Brio"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 92,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "delayed",
            "desc": "Constructs a new brio that will cleanup afer the set amount of time",
            "params": [
                {
                    "name": "time",
                    "desc": "",
                    "lua_type": "number"
                },
                {
                    "name": "...",
                    "desc": "Brio values",
                    "lua_type": "any"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Brio"
                }
            ],
            "function_type": "static",
            "since": "3.6.0",
            "source": {
                "line": 104,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "GetDiedSignal",
            "desc": "Gets a signal that will fire when the Brio dies. If the brio is already dead\ncalling this method will error.\n\n:::info\nCalling this while the brio is already dead will throw a error.\n:::\n\n```lua\nlocal brio = Brio.new(\"a\", \"b\")\nbrio:GetDiedSignal():Connect(function()\n\tprint(\"Brio died\")\nend)\n\nbrio:Kill() --> Brio died\nbrio:Kill() -- no output\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Signal"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 132,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "IsDead",
            "desc": "Returns true is the brio is dead.\n\n```lua\nlocal brio = Brio.new(\"a\", \"b\")\nprint(brio:IsDead()) --> false\n\nbrio:Kill()\n\nprint(brio:IsDead()) --> true\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 159,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "ErrorIfDead",
            "desc": "Throws an error if the Brio is dead.\n\n```lua\nbrio.DEAD:ErrorIfDead() --> ERROR: [Brio.ErrorIfDead] - Brio is dead\n```",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 170,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "ToMaid",
            "desc": "Constructs a new Maid which will clean up when the brio dies.\nWill error if the Brio is dead.\n\n:::info\nCalling this while the brio is already dead will throw a error.\n:::\n\n```lua\nlocal brio = Brio.new(\"a\")\nlocal maid = brio:ToMaid()\nmaid:GiveTask(function()\n\tprint(\"Cleaning up!\")\nend)\nbrio:Kill() --> Cleaning up!\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Maid"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 195,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "GetValue",
            "desc": "If the brio is not dead, will return the values unpacked from the brio.\n\n:::info\nCalling this while the brio is already dead will throw a error. Values should\nnot be used past the lifetime of the brio, and can be considered invalid.\n:::\n\n```lua\nlocal brio = Brio.new(\"a\", 1, 2)\nprint(brio:GetValue()) --> \"a\" 1 2\nbrio:Kill()\n\nprint(brio:GetValue()) --> ERROR: Brio is dead\n```",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "any"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 229,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "GetPackedValues",
            "desc": "Returns the packed values from table.pack() format",
            "params": [],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "{ n: number, ... T }"
                }
            ],
            "function_type": "method",
            "since": "3.6.0",
            "source": {
                "line": 241,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "Destroy",
            "desc": "Kills the Brio.\n\n:::info\nYou can call this multiple times and it will not error if the brio is dead.\n:::\n\n```lua\nlocal brio = Brio.new(\"hi\")\nprint(brio:GetValue()) --> \"hi\"\nbrio:Kill()\n\nprint(brio:GetValue()) --> ERROR: Brio is dead\n```",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 262,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        },
        {
            "name": "Kill",
            "desc": "Alias for Destroy.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 284,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        }
    ],
    "properties": [
        {
            "name": "DEAD",
            "desc": "An already dead brio which may be used for identity purposes.\n\n```lua\nprint(Brio.DEAD:IsDead()) --> true\n```",
            "lua_type": "Brio",
            "source": {
                "line": 296,
                "path": "src/brio/src/Shared/Brio.lua"
            }
        }
    ],
    "types": [],
    "name": "Brio",
    "desc": "Brios wrap a value (or tuple of values) and are used to convey the lifetime of that\nobject. The brio is better than a maid, by providing the following constraints:\n\n- Can be in 2 states, dead or alive.\n- While alive, can retrieve values.\n- While dead, retrieving values is forbidden.\n- Died will fire once upon death.\n\nBrios encapsulate the \"lifetime\" of a valid resource. Unlike a maid, they\n- Can only die once, ensuring duplicate calls never occur.\n- Have less memory leaks. Memory leaks in maids can occur when use of the maid occurs\n  after the cleanup of the maid has occured, in certain race conditions.\n- Cannot be reentered, i.e. cannot retrieve values after death.\n\n:::info\nCalling `brio:Destroy()` or `brio:Kill()` after death does nothing. Brios cannot\nbe resurrected.\n:::\n\nBrios are useful for downstream events where you want to emit a resource. Typically\nbrios should be killed when their source is killed. Brios are intended to be merged\nwith downstream brios so create a chain of reliable resources.\n\n```lua\nlocal brio = Brio.new(\"a\", \"b\")\nprint(brio:GetValue()) --> a b\nprint(brio:IsDead()) --> false\n\nbrio:GetDiedSignal():Connect(function()\n\tprint(\"Hello from signal!\")\nend)\nbrio:ToMaid():GiveTask(function()\n\tprint(\"Hello from maid cleanup!\")\nend)\nbrio:Kill()\n--> Hello from signal!\n--> Hello from maid cleanup!\n\nprint(brio:IsDead()) --> true\nprint(brio:GetValue()) --> ERROR: Brio is dead\n```\n\n## Design philosophy\n\nBrios are designed to solve this issue where we emit an object with a lifetime associated with it from an\nObservable stream. This resource is only valid for some amount of time (for example, while the object is\nin the Roblox data model).\n\nIn order to know how long we can keep this object/use it, we wrap the object with a Brio, which denotes\n the lifetime of the object.\n\nModeling this with pure observables is very tricky because the subscriber will have to also monitor/emit\na similar object with less clear conventions. For example  an observable that emits the object, and then nil on death.",
    "source": {
        "line": 58,
        "path": "src/brio/src/Shared/Brio.lua"
    }
}