# Outputs

`Out` is a function that returns keys to use when hydrating or creating an instance. Those keys let you output a property's value to a `Value` object.

    local name = scope:Value()

    local thing = scope:New "Part" {
        [Out "Name"] = name

    print(peek(name)) --> Part

    thing.Name = "Jimmy"
    print(peek(name)) --> Jimmy

## Usage¶

`Out` doesn't need a scope - import it into your code from Fusion directly.

    local Out = Fusion.Out

When you call `Out` with a property name, it will return a special key:

    local key = Out("Activated")

When used in a property table, you can pass in a `Value` object. It will be set to the value of the property, and when the property changes, it will be set to the new value:

    local name = scope:Value()

    local thing = scope:New "Part" {
        [Out("Name")] = name

    print(peek(name)) --> Part

    thing.Name = "Jimmy"
    print(peek(name)) --> Jimmy

If you're using quotes `'' ""` for the event name, the extra parentheses `()` are optional:

    local thing = scope:New "Part" {
        [Out "Name"] = name

## Two-Way Binding¶

By default, `Out` only _outputs_ changes to the property. If you set the value to something else, the property remains the same:

    local name = scope:Value()

    local thing = scope:New "Part" {
        [Out "Name"] = name -- When `thing.Name` changes, set `name`

    print(thing.Name, peek(name)) --> Part Part
    name:set("NewName")
    task.wait()
    print(thing.Name, peek(name)) --> Part NewName

If you want the value to both _change_ and _be changed_ by the property, you need to explicitly say so:

    local name = scope:Value()

    local thing = scope:New "Part" {
        Name = name -- When `name` changes, set `thing.Name`
        [Out "Name"] = name -- When `thing.Name` changes, set `name`

    print(thing.Name, peek(name)) --> Part Part
    name:set("NewName")
    task.wait()
    print(thing.Name, peek(name)) --> NewName NewName

This is known as two-way binding. Most of the time you won't need it, but it can come in handy when working with some kinds of UI - for example, a text box that users can write into, but which can also be modified by your scripts.

Back to top


#  Out -> [SpecialKey](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) ¶

    function Fusion.Out(
        propertyName: string
    ): SpecialKey

Given an property name, returns a [special key](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) which can output values from properties of that name.

When paired with a [value object](https://elttob.uk/Fusion/0.3/api-reference/state/types/value) in a [property table](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/propertytable), the special key sets the value when the property changes.

## Parameters¶

### propertyName  : string  ¶

The name of the property that the special key should target.

## Returns  -> [SpecialKey](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) ¶

A special key for outputting values from properties of that name.

## Learn More¶

  * [Outputs tutorial](https://elttob.uk/Fusion/0.3/tutorials/roblox/outputs)

Back to top


# Installing Fusion

## Install via Roblox¶

If you are creating Luau experiences in Roblox Studio, then you can import a Roblox model file containing Fusion.

  * Head over to [Fusion's 'Releases' page](https://github.com/Elttob/Fusion/releases).
  * Click the 'Assets' dropdown to view the downloadable files:

  * Click on the `Fusion.rbxm` file to download it. This model contains Fusion.

  * Head into Roblox Studio to import the model; if you're just following the tutorials, an empty baseplate will do.
  * Right-click on `ReplicatedStorage`, and select 'Insert from File':

  * Select the `Fusion.rbxm` file you just downloaded. Y
  * You should see a 'Fusion' module script appear in `ReplicatedStorage`!

Now, you can create a script for testing:

  * Create a `LocalScript` in `StarterGui` or `StarterPlayerScripts`.
  * Remove the default code, and paste the following code in:

    1
    2

    local ReplicatedStorage = game:GetService("ReplicatedStorage")
    local Fusion = require(ReplicatedStorage.Fusion)

  * Press 'Play' - if there are no errors, everything was set up correctly!

## Install as Source Code¶

If you're using pure Luau, or if you're synchronising external files into Roblox Studio, then you can use Fusion's source code directly.

  * Head over to [Fusion's 'Releases' page](https://github.com/Elttob/Fusion/releases).
  * Under 'Assets', download `Source code (zip)`. Inside is a copy of the Fusion GitHub repository.
  * Inside the zip, copy the `src` folder - it may be inside another folder.
  * Paste the `src` folder into your local project, wherever you keep your libraries
    * For example, you might paste it inside a `lib` or `shared` folder.
  * Rename the pasted folder from `src` to `Fusion`.

Once everything is set up, you should be able to `require()` Fusion in one of the following ways:

    -- Rojo
    local Fusion = require(ReplicatedStorage.Fusion)

    -- darklua
    local Fusion = require("../shared/Fusion")

    -- vanilla Luau
    local Fusion = require("../shared/Fusion/init.luau")

Back to top


# Callbacks

Normally, components are controlled by the code creating them. This is called top-down control, and is the primary flow of control in Fusion.

However, sometimes components need to talk back to their controlling code, for example to report when button clicks occur.

## In Luau¶

Callbacks are functions which you pass into other functions. They're useful because they allow the function to 'call back' into your code, so your code can do something in response:

    local function printMessage()
        print("Hello, world!")
    end

    -- Here, we're passing `printMessage` as a callback
    -- `task.delay` will call it after 5 seconds
    task.delay(5, printMessage)

If your function accepts a callback, then you can call it like any other function. Luau will execute the function, then return to your code.

In this example, the `fiveTimes` function calls a callback five times:

Luau codeOutput

    local function fiveTimes(
        callback: (number) -> ()
    )
        for x=1, 5 do
            callback(x)
        end
    end

    fiveTimes(function(num)
        print("The number is", num)
    end)

    The number is 1
    The number is 2
    The number is 3
    The number is 4
    The number is 5

## In Fusion¶

Components can use callbacks the same way. Consider this button component; when the button is clicked, the button needs to run some external code:

    local function Button(
        scope: Fusion.Scope,
        props: {
            Position: UsedAs<UDim2>?,
            Size: UsedAs<UDim2>?,
            Text: UsedAs<string>?
    )
        return scope:New "TextButton" {
            BackgroundColor3 = Color3.new(0.25, 0.5, 1),
            Position = props.Position,
            Size = props.Size,

            Text = props.Text,
            TextColor3 = Color3.new(1, 1, 1),

            [OnEvent "Activated"] = -- ???
    end

It can ask the controlling code to provide an `OnClick` callback in `props`.

    local button = scope:Button {
        Text = "Hello, world!",
        OnClick = function()
            print("The button was clicked")
        end

Assuming that callback is passed in, the callback can be passed directly into `[OnEvent]`, because `[OnEvent]` accepts functions. It can even be optional - Luau won't add the key to the table if the value is `nil`.

    local function Button(
        scope: Fusion.Scope,
        props: {
            Position: UsedAs<UDim2>?,
            Size: UsedAs<UDim2>?,
            Text: UsedAs<string>?,
            OnClick: (() -> ())?
    )
        return scope:New "TextButton" {
            BackgroundColor3 = Color3.new(0.25, 0.5, 1),
            Position = props.Position,
            Size = props.Size,

            Text = props.Text,
            TextColor3 = Color3.new(1, 1, 1),

            [OnEvent "Activated"] = props.OnClick
    end

Alternatively, we can call `props.OnClick` manually, which is useful if you want to do your own processing first:

    local function Button(
        scope: Fusion.Scope,
        props: {
            Position: UsedAs<UDim2>?,
            Size: UsedAs<UDim2>?,
            Text: UsedAs<string>?,
            Disabled: UsedAs<boolean>?,
            OnClick: (() -> ())?
    )
        return scope:New "TextButton" {
            BackgroundColor3 = Color3.new(0.25, 0.5, 1),
            Position = props.Position,
            Size = props.Size,

            Text = props.Text,
            TextColor3 = Color3.new(1, 1, 1),

            [OnEvent "Activated"] = function()
                if props.OnClick ~= nil and not peek(props.Disabled) then
                    props.OnClick()
                end
            end
    end

This is the primary way components talk to their controlling code in Fusion.

## Children Callbacks¶

There's a special kind of callback that's often used when you need more control over the children you're putting inside of a component.

When your component asks for `[Children]`, the controlling code will construct some children for you ahead of time, and pass it into that `[Children]` key. You don't have any control over what that process looks like.

    -- This snippet...
    local dialog = scope:Dialog {
        [Children] = {
            scope:Button {
                Text = "Hello, world!"
            },
            scope:Text {
                Text = "I am pre-fabricated!"

    -- ...is equivalent to this code.
    local children = {
        scope:Button {
            Text = "Hello, world!"
        },
        scope:Text {
            Text = "I am pre-fabricated!"

    local dialog = scope:Dialog {
        [Children] = children

However, if your component asks for a callback instead, you can create those children on demand, as many times as you'd like, with whatever parameters you want to pass in.

This callback should be given a descriptive name like `Build`, `Render`, or whatever terminology fits your code base. Try and be consistent across all of your components.

    local dialog = scope:Dialog {
        -- Use a `scope` parameter here so that the component can change when these
        -- children are destroyed if it needs to. This is especially important for
        -- components that create multiple sets of children over time.
        Build = function(scope)
            return {
                scope:Button {
                    Text = "Hello, world!"
                },
                scope:Text {
                    Text = "I am created on the fly!"
        end

Warning

Don't use `[Children]` to store a function. In general, avoid using special keys unless you're actually passing the values through, because changing how a special key appears to behave can make code confusing to follow.

In this case, using a dedicated naming convention like `Build` ensures that users understand that their children are not being created ahead of time.

Children callbacks are especially useful if the controlling code needs more information to build the rest of the UI. For example, you might want to share some layout information so children can fit into the component more neatly.

    local dialog = scope:Dialog {
        Build = function(scope, textSize)
            return {
                scope:Button {
                    Text = "Hello, world!",
                    TextSize = textSize
                },
                scope:Text {
                    Text = "I am created on the fly!",
                    TextSize = textSize
        end

This is also useful for [sharing values to all children](https://elttob.uk/Fusion/0.3/tutorials/best-practices/sharing-values), which will be covered on a later page.

Back to top


# Cookbook¶

Oftentimes, you might be stuck on a small problem. You want to create something specific, but don't know how to do it with Fusion's tools.

The cookbook can help with that! It's a collection of snippets which show you how to do various small tasks with Fusion, like processing arrays, applying animations and responding to different events.

## Navigation¶

Using the sidebar to the left, you can browse all of the cookbook examples by name.

Back to top


# Hydration

Intent to replace

While the contents of this page still apply (and are useful for explaining other features), `Hydrate` itself will be replaced by other primitives in the near future. [See this issue on GitHub for further details.](https://github.com/dphfox/Fusion/issues/206)

The process of connecting your scripts to a pre-made UI template is known as _hydration_. This is where logic in your scripts translate into UI effects, for example setting a message inside a TextLabel, moving menus around, or showing and hiding buttons.

Screenshot: GameUIDatabase (Halo Infinite)

Fusion provides a `Hydrate` function for hydrating an instance using a table of properties. If you pass in Fusion objects, changes will be applied immediately:

    local showUI = scope:Value(false)

    local ui = scope:Hydrate(StarterGui.Template:Clone()) {
        Name = "MainGui",
        Enabled = showUI

    print(ui.Name) --> MainGui
    print(ui.Enabled) --> false

    showUI:set(true)
    task.wait() -- important: changes are applied on the next frame!
    print(ui.Enabled) --> true

## Usage¶

The `Hydrate` function is called in two parts. First, call the function with the instance you want to hydrate, then pass in the property table:

    local instance = workspace.Part

    scope:Hydrate(instance)({
        Color = Color3.new(1, 0, 0)
    })

If you're using curly braces `{}` to pass your properties in, the extra parentheses `()` are optional:

    local instance = workspace.Part

    -- This only works when you're using curly braces {}!
    scope:Hydrate(instance) {
        Color = Color3.new(1, 0, 0)

`Hydrate` returns the instance you give it, so you can use it in declarations:

    local instance = scope:Hydrate(workspace.Part) {
        Color = Color3.new(1, 0, 0)

If you pass in constant values for properties, they'll be applied to the instance directly. However, if you pass in a Fusion object (like `Value`), then changes will be applied immediately:

    local message = scope:Value("Loading...")

    scope:Hydrate(PlayerGui.LoadingText) {
        Text = message

    print(PlayerGui.Message.Text) --> Loading...

    message:set("All done!")
    task.wait() -- important: changes are applied on the next frame!
    print(PlayerGui.Message.Text) --> All done!

Back to top


#  PropertyTable ¶

    export type PropertyTable = {[string | SpecialKey]: unknown}

A table of named instance properties and [special keys](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey), which can be passed to [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new) to create an instance.

This type can be overly generic

In most cases, you should know what properties your code is looking for. In those cases, you should prefer to list out the properties explicitly, to document what your code needs.

You should only use this type if you don't know what properties your code will accept.

Back to top


#  scoped -> [Scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope)<T> ¶

    function Fusion.scoped<Methods...>(
        ...: (Methods & {})...
    ): Scope<merge(Methods)>

Returns a blank [scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope). Any method tables passed in as arguments are merged together, and used as the `__index` of the new scope, such that they can be called with method notation on the created scope.

Pseudo type

Luau doesn't have adequate syntax to represent this function.

Scopes are not unique

Fusion can recycle old unused scopes. This helps make scopes more lightweight, but it also means they don't uniquely belong to any part of your program.

As a result, you shouldn't hold on to scopes after they've been cleaned up, and you shouldn't use them as unique identifiers anywhere.

## Parameters¶

### ...  : Methods & {}  ¶

A series of tables, ideally including functions which take a scope as their first parameter. Those functions will turn into methods on the scope.

## Returns  -> [Scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope)<T> ¶

A blank scope with the specified methods.

## Learn More¶

  * [Scopes tutorial](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/scopes)

Back to top


#  Attribute -> [SpecialKey](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) ¶

    function Fusion.Attribute(
        attributeName: string
    ): SpecialKey

Given an attribute name, returns a [special key](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) which can modify attributes of that name.

When paired with a value in a [property table](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/propertytable), the special key sets the attribute to that value.

## Parameters¶

### attributeName  : string  ¶

The name of the attribute that the special key should target.

## Returns  -> [SpecialKey](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) ¶

A special key for modifying attributes of that name.

Back to top


# New Instances

Fusion provides a `New` function when you're hydrating newly-made instances. It creates a new instance, applies some default properties, then hydrates it with a property table.

    local message = scope:Value("Hello there!")

    local ui = scope:New "TextLabel" {
        Name = "Greeting",
        Parent = PlayerGui.ScreenGui,

        Text = message

    print(ui.Name) --> Greeting
    print(ui.Text) --> Hello there!

    message:set("Goodbye friend!")
    task.wait() -- important: changes are applied on the next frame!
    print(ui.Text) --> Goodbye friend!

## Usage¶

The `New` function is called in two parts. First, call the function with the type of instance, then pass in the property table:

    local instance = scope:New("Part")({
        Parent = workspace,
        Color = Color3.new(1, 0, 0)
    })

If you're using curly braces `{}` for your properties, and quotes `'' ""` for your class type, the extra parentheses `()` are optional:

    -- This only works when you're using curly braces {} and quotes '' ""!
    local instance = scope:New "Part" {
        Parent = workspace,
        Color = Color3.new(1, 0, 0)

By design, `New` works just like `Hydrate` \- it will apply properties the same way. [See the Hydrate tutorial to learn more.](https://elttob.uk/Fusion/0.3/tutorials/roblox/hydration)

## Default Properties¶

When you create an instance using `Instance.new()`, Roblox will give it some default properties. However, these tend to be outdated and aren't useful for most people, leading to repetitive boilerplate needed to disable features that nobody wants to use.

The `New` function will apply some of it's own default properties to fix this. For example, by default borders on UI are disabled, automatic colouring is turned off and default content is removed.

For a complete list, [take a look at Fusion's default properties file.](https://github.com/Elttob/Fusion/blob/main/src/Instances/defaultProps.luau)

Back to top


# Loading Spinner

This example implements a procedural spinning animation using Fusion's Roblox APIs.

## Overview¶

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61

    local RunService = game:GetService("RunService")

    local Fusion = -- initialise Fusion here however you please!
    local scoped = Fusion.scoped
    local Children = Fusion.Children
    type UsedAs<T> = Fusion.UsedAs<T>

    local SPIN_DEGREES_PER_SECOND = 180
    local SPIN_SIZE = 50

    local function Spinner(
        scope: Fusion.Scope,
        props: {
            Layout: {
                LayoutOrder: UsedAs<number>?,
                Position: UsedAs<UDim2>?,
                AnchorPoint: UsedAs<Vector2>?,
                ZIndex: UsedAs<number>?
            },
            CurrentTime: UsedAs<number>,
    ): Fusion.Child
        return scope:New "ImageLabel" {
            Name = "Spinner",

            LayoutOrder = props.Layout.LayoutOrder,
            Position = props.Layout.Position,
            AnchorPoint = props.Layout.AnchorPoint,
            ZIndex = props.Layout.ZIndex,

            Size = UDim2.fromOffset(SPIN_SIZE, SPIN_SIZE),

            BackgroundTransparency = 1,
            Image = "rbxassetid://your-loading-spinner-image", -- replace this!

            Rotation = scope:Computed(function(use)
                return (use(props.CurrentTime) * SPIN_DEGREES_PER_SECOND) % 360
            end)
    end

    -- Don't forget to pass this to `doCleanup` if you disable the script.
    local scope = scoped(Fusion, {
        Spinner = Spinner
    })

    local currentTime = scope:Value(os.clock())
    table.insert(scope,
        RunService.RenderStepped:Connect(function()
            currentTime:set(os.clock())
        end)
    )

    local spinner = scope:Spinner {
        Layout = {
            Position = UDim2.fromScale(0.5, 0.5),
            AnchorPoint = Vector2.new(0.5, 0.5),
            Size = UDim2.fromOffset(50, 50)
        },
        CurrentTime = currentTime

## Explanation¶

The `Spinner` components implements the animation for the loading spinner. It's largely a standard Fusion component definition.

The main thing to note is that it asks for a `CurrentTime` property.

    local function Spinner(
        scope: Fusion.Scope,
        props: {
            Layout: {
                LayoutOrder: UsedAs<number>?,
                Position: UsedAs<UDim2>?,
                AnchorPoint: UsedAs<Vector2>?,
                ZIndex: UsedAs<number>?
            },
            CurrentTime: UsedAs<number>,
    ): Fusion.Child

The `CurrentTime` is used to drive the rotation of the loading spinner.

            Rotation = scope:Computed(function(use)
                return (use(props.CurrentTime) * SPIN_DEGREES_PER_SECOND) % 360
            end)

That's all that's required for the `Spinner` component.

Later on, the example creates a `Value` object that will store the current time, and starts a process to keep it up to date.

    local currentTime = scope:Value(os.clock())
    table.insert(scope,
        RunService.RenderStepped:Connect(function()
            currentTime:set(os.clock())
        end)
    )

This can then be passed in as `CurrentTime` when the `Spinner` is created.

    local spinner = scope:Spinner {
        Layout = {
            Position = UDim2.fromScale(0.5, 0.5),
            AnchorPoint = Vector2.new(0.5, 0.5),
            Size = UDim2.fromOffset(50, 50)
        },
        CurrentTime = currentTime

Back to top


#  Observer -> [Observer](https://elttob.uk/Fusion/0.3/api-reference/state/types/Observer) ¶

    function Fusion.Observer(
        scope: Scope<unknown>,
        watching: unknown
    ) -> Observer

Constructs and returns a new [observer](https://elttob.uk/Fusion/0.3/api-reference/graph/types/observer).

Use scoped() method syntax

This function is intended to be accessed as a method on a scope:

    local observer = scope:Observer(watching)

## Parameters¶

### scope  : [Scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope)<unknown> ¶

The [scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope) which should be used to store destruction tasks for this object.

### watching  : unknown  ¶

The target that the observer should watch for changes.

Works best with graph objects

While non-[graph object](https://elttob.uk/Fusion/0.3/api-reference/graph/types/graphobject) values are accepted for compatibility, they won't be able to trigger updates.

## Returns  -> [Observer](https://elttob.uk/Fusion/0.3/api-reference/state/types/observer) ¶

A freshly constructed observer.

## Learn More¶

  * [Observers tutorial](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/observers)

Back to top


#  Contextual ¶

    export type Contextual<T> = {
        type: "Contextual",
        now: (self) -> T,
        is: (self, newValue: T) -> {
            during: <R, A...>(self, callback: (A...) -> R, A...) -> R

An object representing a widely-accessible value, which can take on different values at different times in different coroutines.

Non-standard type syntax

The above type definition uses `self` to denote methods. At time of writing, Luau does not interpret `self` specially.

## Fields¶

### type  : "Contextual"  ¶

A type string which can be used for runtime type checking.

## Methods¶

### now  -> T  ¶

    function Contextual:now(): T

Returns the current value of this contextual. This varies based on when the function is called, and in what coroutine it was called.

### is/during  -> R  ¶

    function Contextual:is(
        newValue: T
    ): {
        during: <R, A...>(
            self,
            callback: (A...) -> R,
            A...
        ) -> R

Runs the `callback` with the arguments `A...` and returns the value the callback returns (`R`). The `Contextual` will appear to be `newValue` in the callback, unless it's overridden by another `:is():during()` call.

## Learn More¶

  * [Sharing Values tutorial](https://elttob.uk/Fusion/0.3/tutorials/best-practices/sharing-values)

Back to top


#  Child ¶

    export type Child = Instance | StateObject<Child> | {[unknown]: Child}

All of the types understood by the [`[Children]`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/children) special key.

## Learn More¶

  * [Parenting tutorial](https://elttob.uk/Fusion/0.3/tutorials/roblox/parenting/)
  * [Instance Handling tutorial](https://elttob.uk/Fusion/0.3/tutorials/best-practices/instance-handling/)

Back to top


#  New -> ([PropertyTable](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/propertytable)) -> Instance  ¶

    function Fusion.New(
        className: string
    ): (
        props: PropertyTable
    ) -> Instance

Given a class name, returns a component for constructing instances of that class.

In the property table, string keys are assigned as properties on the instance. If the value is a [state object](https://elttob.uk/Fusion/0.3/api-reference/state/types/stateobject), it is re-assigned every time the value of the state object changes.

Any [special keys](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) present in the property table are applied to the instance after string keys are processed, in the order specified by their `stage`.

A special exception is made for assigning `Parent`, which is only assigned after the `descendants` stage.

## Parameters¶

### className  : string  ¶

The kind of instance that should be constructed.

## Returns  -> ([PropertyTable](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/propertytable)) -> Instance  ¶

A component that constructs instances of that type, accepting various properties to customise each instance uniquely.

## Learn More¶

  * [New Instances tutorial](https://elttob.uk/Fusion/0.3/tutorials/roblox/new-instances)

Back to top


# Instance Handling

Components are a good fit for Roblox instances. You can assemble complex groups of instances by combining simpler, self-contained parts.

To ensure maximum compatibility, there are a few best practices to consider.

## Returns¶

Anything you return from a component should be supported by [`[Children]`](https://elttob.uk/Fusion/0.3/tutorials/roblox/parenting/).

    -- returns an instance
    return scope:New "Frame" {}

    -- returns an array of instances
    return {
        scope:New "Frame" {},
        scope:New "Frame" {},
        scope:New "Frame" {}

    -- returns a state object containing instances
    return scope:ForValues({1, 2, 3}, function(use, scope, number)
        return scope:New "Frame" {}
    end)

    -- mix of arrays, instances and state objects
    return {
        scope:New "Frame" {},
            scope:New "Frame" {},
            scope:ForValues( ... )
        scope:ForValues( ... )

Returning multiple values is fragile

Don't return multiple values directly from your function. When a function returns multiple values directly, the extra returned values can easily get lost.

    local function BadThing(scope, props)
        -- returns *multiple* instances (not surrounded by curly braces!)
        return
            scope:New "Frame" {},
            scope:New "Frame" {},
            scope:New "Frame" {}
    end

    local things = {
        -- Luau doesn't let you add multiple returns to a list like this.
        -- Only the first Frame will be added.
        scope:BadThing {},
        scope:New "TextButton" {}
    print(things) --> { Frame, TextButton }

Instead, you should return them inside of an array. Because the array is a single return value, it won't get lost.

If your returns are compatible with `[Children]` like above, you can insert a component anywhere you'd normally insert an instance.

You can pass in one component on its own...

    local ui = scope:New "ScreenGui" {
        [Children] = scope:Button {
            Text = "Hello, world!"

...you can include components as part of an array..

    local ui = scope:New "ScreenGui" {
        [Children] = {
            scope:New "UIListLayout" {},

            scope:Button {
                Text = "Hello, world!"
            },

            scope:Button {
                Text = "Hello, again!"

...and you can return them from state objects, too.

    local stuff = {"Hello", "world", "from", "Fusion"}

    local ui = scope:New "ScreenGui" {
        [Children] = {
            scope:New "UIListLayout" {},

            scope:ForValues(stuff, function(use, scope, text)
                return scope:Button {
                    Text = text
            end)

## Containers¶

Some components, for example pop-ups, might contain lots of different content:

Ideally, you would be able to reuse the pop-up 'container', while placing your own content inside.

The simplest way to do this is to pass instances through to `[Children]`. For example, if you accept a table of `props`, you can add a `[Children]` key:

    local function PopUp(
        scope: Fusion.Scope,
        props: {
            [typeof(Children)]: Fusion.Children
    )
        return scope:New "Frame" {
            [Children] = props[Children]
    end

Accepting multiple instances

If you have multiple 'slots' where you want to pass through instances, you can make other properties and give them the `Fusion.Children` type.

Later on, when a pop-up is created, instances can now be parented into that pop-up:

    scope:PopUp {
        [Children] = {
            scope:Label {
                Text = "New item collected"
            },
            scope:ItemPreview {
                Item = Items.BRICK
            },
            scope:Button {
                Text = "Add to inventory"

If you need to add other instances, you can still use arrays and state objects as normal. You can include instances you're given, in exactly the same way you would include any other instances.

    scope:New "Frame" {
        -- ... some other properties ...

        [Children] = {
            -- the component provides some children here
            scope:New "UICorner" {
                CornerRadius = UDim.new(0, 8)
            },

            -- include children from outside the component here
            props[Children]

Back to top


#  peek : [Use](https://elttob.uk/Fusion/0.3/api-reference/state/types/use) ¶

    function Fusion.peek<T>(
        target: UsedAs<T>
    ): T

Extracts a value of type `T` from its input.

This is a general-purpose implementation of [`Use`](https://elttob.uk/Fusion/0.3/api-reference/state/types/use). It does not do any extra processing or book-keeping beyond what is required to determine the returned value.

Specific implementations

If you're given a specific implementation of `Use` by an API, it's highly likely that you are expected to use that implementation instead of `peek()`.

This applies to reusable code too. It's often best to ask for a `Use` callback if your code needs to extract values, so an appropriate implementation can be passed in.

Alternatively for reusable code, you can avoid extracting values entirely, and expect the user to do it prior to calling your code. This can work well if you unconditionally use all inputs, but beware that you may end up extracting more values than you need - this can have performance implications.

## Parameters¶

### target  : [UsedAs](https://elttob.uk/Fusion/0.3/api-reference/state/types/usedas)<T> ¶

The abstract representation of `T` to extract a value from.

## Returns  -> T  ¶

The current value of `T`, derived from `target`.

## Learn More¶

  * [Values tutorial](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/values/)

Back to top


# State

Components can hold their own data privately using state objects. This can be useful, but you should be careful when adding state.

## Creation¶

You can create state objects inside components as you would anywhere else.

    local HOVER_COLOUR = Color3.new(0.5, 0.75, 1)
    local REST_COLOUR = Color3.new(0.25, 0.5, 1)

    local function Button(
        scope: Fusion.Scope,
        props: {
            -- ... some properties ...
    )
        local isHovering = scope:Value(false)

        return scope:New "TextButton" {
            BackgroundColor3 = scope:Computed(function(use)
                return if use(isHovering) then HOVER_COLOUR else REST_COLOUR
            end),

            -- ... ... some more code ...
    end

Because these state objects are made with the same `scope` as the rest of the component, they're destroyed alongside the rest of the component.

## Top-Down Control¶

Remember that Fusion mainly works with a top-down flow of control. It's a good idea to keep that in mind when adding state to components.

When you're making reusable components, it's more flexible if your component can be controlled externally. Components that control themselves entirely are hard to use and customise.

Consider the example of a check box. Each check box often reflects a state object under the hood:

It might _seem_ logical to store the state object inside the check box, but this causes a few problems:

  * because the state is hidden, it's awkward to read and write from outside
  * often, the user already has a state object representing the same setting, so now there's two state objects where one would have sufficed

    local function CheckBox(
        scope: Fusion.Scope,
        props: {
            -- ... some properties ...
    )
        local isChecked = scope:Value(false) -- problematic

        return scope:New "ImageButton" {
            [OnEvent "Activated"] = function()
                isChecked:set(not peek(isChecked))
            end,

            -- ... some more code ...
    end

A _slightly better_ solution is to pass the state object in. This ensures the controlling code has easy access to the state if it needs it. However, this is not a complete solution:

  * the user is forced to store the state in a `Value` object, but they might be computing the value dynamically with other state objects instead
  * the behaviour of clicking the check box is hardcoded; the user cannot intercept the click or toggle a different state

    local function CheckBox(
        scope: Fusion.Scope,
        props: {
            IsChecked: Fusion.Value<boolean> -- slightly better
    )
        return scope:New "ImageButton" {
            [OnEvent "Activated"] = function()
                props.IsChecked:set(not peek(props.IsChecked))
            end,

            -- ... some more code ...
    end

That's why the _best_ solution is to use `UsedAs` to create read-only properties, and add callbacks for signalling actions and events.

  * because `UsedAs` is read-only, it lets the user plug in any data source, including dynamic computations
  * because the callback is provided by the user, the behaviour of clicking the check box is completely customisable

    local function CheckBox(
        scope: Fusion.Scope,
        props: {
            IsChecked: UsedAs<boolean>, -- best
            OnClick: () -> ()
    )
        return scope:New "ImageButton" {
            [OnEvent "Activated"] = function()
                props.OnClick()
            end,

            -- ... some more code ...
    end

The control is always top-down here; the check box's appearance is fully controlled by the creator. The creator of the check box _decides_ to switch the setting when the check box is clicked.

### In Practice¶

Setting up your components in this way makes extending their behaviour incredibly straightforward.

Consider a scenario where you wish to group multiple options under a 'main' check box, so you can turn them all on/off at once.

The appearance of that check box would not be controlled by a single state, but instead reflects the combination of multiple states. Because the code uses `UsedAs`, you can represent this with a `Computed` object.

    local playMusic = scope:Value(true)
    local playSFX = scope:Value(false)
    local playNarration = scope:Value(true)

    local checkBox = scope:CheckBox {
        Text = "Play sounds",
        IsChecked = scope:Computed(function(use)
            local anyChecked = use(playMusic) or use(playSFX) or use(playNarration)
            local allChecked = use(playMusic) and use(playSFX) and use(playNarration)

            if not anyChecked then
                return "unchecked"
            elseif not allChecked then
                return "partially-checked"
            else
                return "checked"
            end
        end)

You can then implement the 'check all'/'uncheck all' behaviour inside `OnClick`:

    local playMusic = scope:Value(true)
    local playSFX = scope:Value(false)
    local playNarration = scope:Value(true)

    local checkBox = scope:CheckBox {
        -- ... same properties as before ...
        OnClick = function()
            local allChecked = peek(playMusic) and peek(playSFX) and peek(playNarration)

            playMusic:set(not allChecked)
            playSFX:set(not allChecked)
            playNarration:set(not allChecked)
        end

Because the check box was written to be flexible, it can handle complex usage easily.

## Best Practices¶

Those examples lead us to the golden rule of reusable components:

Golden Rule

Reusable components should _reflect_ program state. They should not _control_ program state.

At the bottom of the chain of control, components shouldn't be massively responsible. At these levels, reflective components are easier to work with.

As you go up the chain of control, components get broader in scope and less reusable; those places are often suitable for controlling components.

A well-balanced codebase places controlling components at key, strategic locations. They allow higher-up components to operate without special knowledge about what goes on below.

At first, this might be difficult to do well, but with experience you'll have a better intuition for it. Remember that you can always rewrite your code if it becomes a problem!

Back to top


#  Hydrate -> ([PropertyTable](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/propertytable)) -> Instance  ¶

    function Fusion.Hydrate(
        target: Instance
    ): (
        props: PropertyTable
    ) -> Instance

Given an instance, returns a component for binding extra functionality to that instance.

In the property table, string keys are assigned as properties on the instance. If the value is a [state object](https://elttob.uk/Fusion/0.3/api-reference/state/types/stateobject), it is re-assigned every time the value of the state object changes.

Any [special keys](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) present in the property table are applied to the instance after string keys are processed, in the order specified by their `stage`.

A special exception is made for assigning `Parent`, which is only assigned after the `descendants` stage.

Do not overwrite properties

If the instance was previously created with [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new) or previously hydrated, do not assign to any properties that were previously specified in those prior calls. Duplicated assignments can interfere with each other in unpredictable ways.

## Parameters¶

### target  : Instance  ¶

The instance which should be modified.

## Returns  -> ([PropertyTable](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/propertytable)) -> Instance  ¶

A component that hydrates that instance, accepting various properties to build up bindings and operations applied to the instance.

## Learn More¶

  * [Hydration tutorial](https://elttob.uk/Fusion/0.3/tutorials/roblox/hydration)

Back to top


# Sharing Values

Sometimes values are used in far-away parts of the codebase. For example, many UI elements might share theme colours for light and dark theme.

## Globals¶

Typically, values are shared by placing them in modules. These modules can be required from anywhere in the codebase, and their values can be imported into any code.

Values shared in this way are known as _globals_.

Theme.luauSomewhere else

    1
    2
    3
    4
    5
    6
    7
    8
    9

    local Theme = {}

    Theme.colours = {
        background = Color3.fromHex("FFFFFF"),
        text = Color3.fromHex("222222"),
        -- etc.

    return Theme

    1
    2
    3
    4

    local Theme = require("path/to/Theme.luau")

    local textColour = Theme.colours.text
    print(textColour) --> 34, 34, 34

In particular, you can share state objects this way, and every part of the codebase will be able to see and interact with those state objects.

Theme.luauSomewhere else

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    local Fusion = require("path/to/Fusion.luau")

    local Theme = {}

    Theme.colours = {
        background = {
            light = Color3.fromHex("FFFFFF"),
            dark = Color3.fromHex("222222")
        },
        text = {
            light = Color3.fromHex("FFFFFF"),
            dark = Color3.fromHex("222222")
        },
        -- etc.

    function Theme.init(
        scope: Fusion.Scope
    )
        Theme.currentTheme = scope:Value("light")
    end

    return Theme

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19

    local Fusion = require("path/to/Fusion.luau")
    local scoped, peek = Fusion.scoped, Fusion.peek

    local Theme = require("path/to/Theme.luau")

    local function printTheme()
        local theme = Theme.currentTheme
        print(
            peek(theme),
            if typeof(theme) == "string" then "constant" else "state object"
        )
    end

    local scope = scoped(Fusion)
    Theme.init(scope)
    printTheme() --> light state object

    Theme.currentTheme:set("dark")
    printTheme() --> dark state object

Globals are very straightforward to implement and can be useful, but they can quickly cause problems if not used carefully.

### Hidden dependencies¶

When you use a global inside a block of reusable code such as a component, you are making your code dependent on another code file without declaring it to the outside world.

To some extent, this is entirely why using globals is desirable. While it's more 'correct' to accept the `Theme` via the parameters of your function, it often means the `Theme` has to be passed down through multiple layers of functions. This is known as [prop drilling](https://kentcdodds.com/blog/prop-drilling) and is widely considered bad practice, because it clutters up unrelated code with parameters that are only passed through functions.

To avoid prop drilling, globals are often used, which 'hides' the dependency on that external code file. You no longer have to pass it down through parameters. However, relying too heavily on these hidden dependencies can cause your code to behave in surprising, unintuitive ways, or it can obscure what functionality is available to developers using your code.

### Hard-to-locate writes¶

If you write into globals from deep inside your code base, it becomes very hard to figure out where the global is being changed from, which significantly hurts debugging.

Generally, it's best to treat globals as _read-only_. If you're writing to a global, it should be coming from a single well-signposted, easy-to-find place.

You should also keep the principles of [top-down control](https://elttob.uk/Fusion/0.3/tutorials/best-practices/callbacks) in mind; think of globals as 'flowing down' from the root of the program. Globals are best managed from high up in the program, because they have widespread effects, so consider using callbacks to pass control up the chain, rather than managing globals directly from every part of the code base.

### Memory management¶

In addition, globals can complicate memory management. Because every part of your code base can access them, you can't destroy globals until the very end of your program.

In the above example, this is solved with an `init()` method which passes the main scope to `Theme`. Because `init()` is called before anything else that uses `Theme`, the objects that `Theme` creates will be added to the scope first.

When the main scope is cleaned up, `doCleanup()` will destroy things in reverse order. This means the `Theme` objects will be cleaned up last, after everything else in the program has been cleaned up.

This only works if you know that the main script is the only entry point in your program. If you have two scripts running concurrently which try to `init()` the `Theme` module, they will overwrite each other.

### Non-replaceable for testing¶

When your code uses a global, you're hard-coding a connection between your code and that specific global.

This is problematic for testing; unless you're using an advanced testing framework with code injection, it's pretty much impossible to separate your code from that global code, which makes it impossible to replace global values for testing purposes.

For example, if you wanted to write automated tests that verify light theme and dark theme are correctly applied throughout your UI, you can't replace any values stored in `Theme`.

You might be able to write to the `Theme` by going through the normal process, but this fundamentally limits how you can test. For example, you couldn't run a test for light theme and dark theme at the same time.

## Contextuals¶

The main drawback of globals is that they hold one value for all code. To solve this, Fusion introduces _contextual values_ , which can be temporarily changed for the duration of a code block.

To create a contextual, call the `Contextual` function from Fusion. It asks for a default value.

    local myContextual = Contextual("foo")

At any time, you can query its current value using the `:now()` method.

    local myContextual = Contextual("foo")
    print(myContextual:now()) --> foo

You can override the value for a limited span of time using `:is():during()`. Pass the temporary value to `:is()`, and pass a callback to `:during()`.

While the callback is running, the contextual will adopt the temporary value.

    local myContextual = Contextual("foo")
    print(myContextual:now()) --> foo

    myContextual:is("bar"):during(function()
        print(myContextual:now()) --> bar
    end)

    print(myContextual:now()) --> foo

By storing widely-used values inside contextuals, you can isolate different code paths from each other, while retaining the easy, hidden referencing that globals offer. This makes testing and memory management significantly easier, and helps you locate which code is modifying any shared values.

To demonstrate, the `Theme` example can be rewritten to use contextuals.

Theme.luauSomewhere else

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20

    local Fusion = require("path/to/Fusion.luau")
    local Contextual = Fusion.Contextual

    local Theme = {}

    Theme.colours = {
        background = {
            light = Color3.fromHex("FFFFFF"),
            dark = Color3.fromHex("222222")
        },
        text = {
            light = Color3.fromHex("FFFFFF"),
            dark = Color3.fromHex("222222")
        },
        -- etc.

    Theme.currentTheme = Contextual("light")

    return Theme

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24

    local Fusion = require("path/to/Fusion.luau")
    local scoped, peek = Fusion.scoped, Fusion.peek

    local Theme = require("path/to/Theme.luau")

    local function printTheme()
        local theme = Theme.currentTheme:now()
        print(
            peek(theme),
            if typeof(theme) == "string" then "constant" else "state object"
        )
    end

    printTheme() --> light constant

    local scope = scoped(Fusion)
    local override = scope:Value("light")
    Theme.currentTheme:is(override):during(function()
        printTheme()  --> light state object
        override:set("dark")
        printTheme() --> dark state object
    end)

    printTheme() --> light constant

In this rewritten example, `Theme` no longer requires an `init()` function, because - instead of defining a state object globally - `Theme` only defines `"light"` as the default value.

You're expected to replace the default value with a state object when you want to make the theme dynamic. This has a number of benefits:

  * Because the override is time-limited to one span of your code, you can have multiple scripts running at the same time with completely different overrides.

  * It also explicitly places your code in charge of memory management, because you're creating the object yourself.

  * It's easy to locate where changes are coming from, because you can look for the nearest `:is():during()` call. Optionally, you could share a limited, read-only version of the value, while retaining private access to write to the value wherever you're overriding the contextual from.

  * Testing becomes much simpler; you can override the contextual for different parts of your testing, without ever having to inject code, and without altering how you read and override the contextual in your production code.

It's still possible to run into issues with contextuals, though.

  * You're still hiding a dependency of your code, which can still lead to confusion and obscuring available features, just the same as globals.
  * Unlike globals, contextuals are time-limited. If you connect to an event or start a delayed task, you won't be able to access the value anymore. Instead, capture the value at the start of the code block, so you can use it in delayed tasks.

Back to top


#  Contextual -> [Contextual](https://elttob.uk/Fusion/0.3/api-reference/general/types/contextual)<T> ¶

    function Fusion.Contextual<T>(
        defaultValue: T
    ): Contextual<T>

Constructs and returns a new [contextual](https://elttob.uk/Fusion/0.3/api-reference/general/types/contextual).

## Parameters¶

### defaultValue  : T  ¶

The value which `Contextual:now()` should return if no value has been specified by `Contextual:is():during()`.

## Returns  -> [Contextual](https://elttob.uk/Fusion/0.3/api-reference/general/types/contextual)<T> ¶

A freshly constructed contextual.

## Learn More¶

  * [Sharing Values tutorial](https://elttob.uk/Fusion/0.3/tutorials/best-practices/sharing-values)

Back to top


#  Errors ¶

Whenever Fusion outputs any errors or messages to the console, it will have a short error ID at the end. This is used to uniquely identify what kind of error or message you're seeing.

Use the search box below to paste in or type an error ID, and it will scroll to the details for you.

## callbackError¶

    Error in callback: attempt to perform arithmetic (add) on number and string

**Thrown by:** [`Computed`](https://elttob.uk/Fusion/0.3/api-reference/state/members/computed), [`ForKeys`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forkeys), [`ForValues`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forvalues), [`ForPairs`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forpairs), [`Contextual`](https://elttob.uk/Fusion/0.3/api-reference/memory/members/contextual)

Fusion ran a function you specified, but the function threw an error that Fusion couldn't handle.

The error includes a more specific message which can be used to diagnose the issue.

## cannotAssignProperty¶

    The class type 'Foo' has no assignable property 'Bar'.

**Thrown by:** [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new), [`Hydrate`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/hydrate)

You tried to set a property on an instance, but the property can't be assigned to for some reason. This could be because the property doesn't exist, or because it's locked by Roblox to prevent edits.

Check your privileges

Different scripts may have different privileges - for example, plugins will be allowed more privileges than in-game scripts. Make sure you have the necessary privileges to assign to your properties!

## cannotConnectChange¶

    The Frame class doesn't have a property called 'Foo'.

**Thrown by:** [`OnChange`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/onchange)

You tried to connect to a property change event, but the property you specify doesn't exist on the instance.

## cannotConnectEvent¶

    The Frame class doesn't have an event called 'Foo'.

**Thrown by:** [`OnEvent`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/onevent)

You tried to connect to an event on an instance, but the event you specify doesn't exist on the instance.

## cannotCreateClass¶

    Can't create a new instance of class 'EditableImage'.

**Thrown by:** [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new)

You attempted to create a type of instance that Fusion can't create.

Beta features

Some instances are only creatable when you have certain Studio betas enabled. Check your Beta Features tab to ensure that beta features aren't causing the issue.

## cannotDepend¶

    Observer can't depend on Observer.

**Thrown by:** [`Observer`](https://elttob.uk/Fusion/0.3/api-reference/graph/members/observer)

You attempted to form a dependency between two [graph objects](https://elttob.uk/Fusion/0.3/api-reference/graph/types/graphobject), but either the dependency set or dependent set were frozen.

You might be trying to connect them in the wrong order, or the objects might not be designed to have dependents or dependencies.

## cleanupWasRenamed¶

    `Fusion.cleanup` was renamed to `Fusion.doCleanup`. This will be an error in
    future versions of Fusion.

**Thrown by:** [`doCleanup`](https://elttob.uk/Fusion/0.3/api-reference/memory/members/docleanup)

You attempted to use `cleanup()` in Fusion 0.3, which replaces it with the `doCleanup()` method.

## destroyedTwice¶

    `doCleanup()` was given something that it is already cleaning up. Unclear how to
    proceed.

**Thrown by:** [`doCleanup`](https://elttob.uk/Fusion/0.3/api-reference/memory/members/doCleanup)

You called `doCleanup()` on a function or object which carried some code. When that code was run, it attempted to call `doCleanup()` on the same thing you called with.

Usually, this would result in an infinite loop, because the same code would try to clean itself up over and over again. Because cleanup tasks are only meant to run once, this is invalid behaviour and so this error is thrown instead.

Ensure your code is the rightful owner of scopes that it is trying to clean up. In particular, avoid cleaning up scopes you receive from elsewhere, unless you and the original provider of the scope agree to transfer the responsibility of cleaning up the scope.

## destructorRedundant¶

    Computed destructors no longer do anything. If you wish to run code on destroy,
    `table.insert` a function into the `scope` argument. See discussion #292 on
    GitHub for advice.

**Thrown by:** [`Computed`](https://elttob.uk/Fusion/0.3/api-reference/state/members/computed), [`ForKeys`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forkeys), [`ForValues`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forvalues), [`ForPairs`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forpairs)

**Related discussions:** [`#292`](https://github.com/dphfox/Fusion/discussions/292)

You passed an extra parameter to the constructor, which has historically been interpreted as a function that runs when a value is cleaned up.

This mechanism has been replaced by [scopes](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/scopes).

## forKeyCollision¶

    The key '6' was returned multiple times simultaneously, which is not allowed in
    `For` objects.

**Thrown by:** [`ForKeys`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forkeys), [`ForPairs`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forpairs)

When called with different items from the table, the same key was returned for both of them. This is not allowed, because keys have to be unique in a table.

## invalidAttributeChangeHandler¶

    The change handler for the 'Active' attribute must be a function.

**Thrown by:** [`AttributeChange`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/attributechange)

`AttributeChange` expected you to provide a function for it to run when the attribute changes, but you provided something other than a function.

For example, you might have accidentally provided `nil`.

## invalidAttributeOutType¶

    [AttributeOut] properties must be given Value objects.

**Thrown by:** [`AttributeOut`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/attributeout)

`AttributeOut` expected you to give it a [value](https://elttob.uk/Fusion/0.3/api-reference/state/members/value), but you gave it something else.

## invalidChangeHandler¶

    The change handler for the 'AbsoluteSize' property must be a function.

**Thrown by:** [`OnChange`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/onchange)

`OnChange` expected you to provide a function for it to run when the property changes, but you provided something other than a function.

For example, you might have accidentally provided `nil`.

## invalidEventHandler¶

    The handler for the 'MouseEnter' event must be a function.

**Thrown by:** [`OnEvent`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/onevent)

`OnEvent` expected you to provide a function for it to run when the event is fired, but you provided something other than a function.

For example, you might have accidentally provided `nil`.

## invalidOutProperty¶

    The Frame class doesn't have a property called 'MouseButton1Down'.

**Thrown by:** [`Out`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/out)

The property that you tried to output doesn't exist on the instance that `Out` was used with.

## invalidOutType¶

    [Out] properties must be given Value objects.

**Thrown by:** [`Out`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/out)

`Out` expected you to give it a [value](https://elttob.uk/Fusion/0.3/api-reference/state/members/value), but you gave it something else.

## invalidPropertyType¶

    'Frame.BackgroundColor3' expected a 'Color3' type, but got a 'Vector3' type.

**Thrown by:** [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new), [`Hydrate`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/hydrate)

You attempted to assign a value to a Roblox instance's property, but the assignment threw an error because that property doesn't accept values of that type.

## invalidRefType¶

    Instance refs must be Value objects.

**Thrown by:** [`Ref`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/ref)

`Ref` expected you to give it a [value](https://elttob.uk/Fusion/0.3/api-reference/state/members/value), but you gave it something else.

## invalidSpringDamping¶

    The damping ratio for a spring must be >= 0. (damping was -1.00)

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/spring)

You provided a damping ratio that the spring doesn't support, for example `NaN`, or a negative damping implying negative friction.

## invalidSpringSpeed¶

    The speed of a spring must be >= 0. (speed was NaN)

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/spring)

You provided a speed multiplier that the spring doesn't support, for example `NaN` or a negative speed implying the spring moves backwards through time.

## mergeConflict¶

    Multiple definitions for 'Observer' found while merging.

**Thrown by:** [`scoped`](https://elttob.uk/Fusion/0.3/api-reference/memory/members/scoped)

Fusion tried to merge together multiple tables, but a key was found in more than one of the tables, and it's unclear which one you intended to have in the final merged result.

This can happen subtly with methods such as [`scoped()`](https://elttob.uk/Fusion/0.3/api-reference/memory/members/scoped) which automatically merge together all of their arguments.

## mistypedSpringDamping¶

    The damping ratio for a spring must be a number. (got a string)

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/spring)

You provided a damping ratio that the spring couldn't understand. Damping ratio has to be a number.

## mistypedSpringSpeed¶

    The speed of a spring must be a number. (got a string)

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/spring)

You provided a speed multiplier that the spring couldn't understand. Speed has to be a number.

## mistypedTweenInfo¶

    The tween info of a tween must be a TweenInfo. (got a table)

**Thrown by:** [`Tween`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/tween)

You provided an easing curve that the tween couldn't understand. The easing curve has to be specified using Roblox's `TweenInfo` data type.

## noTaskScheduler¶

    Fusion is not connected to an external task scheduler.

Fusion depends on a task scheduler being present to perform certain time-related tasks such as deferral, delays, or updating animations. You'll need to define a set of standard task scheduler functions that Fusion can use for those purposes.

Roblox users should never see this error, as Fusion automatically connects to Roblox's task scheduling APIs.

## poisonedScope¶

    Attempted to use a scope after it's been destroyed; `doCleanup()` was previously
    called on this scope. Ensure you are not reusing scopes after cleanup.

**Thrown by:** scopes after being passed to [`doCleanup`](https://elttob.uk/Fusion/0.3/api-reference/memory/members/doCleanup)

If you attempt to read from, or write to, a scope that's been destroyed, this message is shown. After a scope has been cleaned up, your code should forget the reference to it, as it is no longer valid.

## possiblyOutlives¶

    The Computed (bound to the PaddingLeft property) will be destroyed before the
    UIPadding instance; the latter is in a different scope that gets destroyed too
    quickly. To fix this, review the order they're created in, and what scopes they
    belong to. See discussion #292 on GitHub for advice.

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/spring), [`Tween`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/tween), [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new), [`Hydrate`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/hydrate), [`Attribute`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/attribute), [`AttributeOut`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/attributeout), [`Out`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/out), [`Ref`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/ref), [`Computed`](https://elttob.uk/Fusion/0.3/api-reference/state/members/computed), [`Observer`](https://elttob.uk/Fusion/0.3/api-reference/state/members/observer)

**Related discussions:** [`#292`](https://github.com/dphfox/Fusion/discussions/292)

If you use an object after it's been destroyed, then your code can break. This mainly happens when one object 'outlives' another object that it's using.

Because [scopes](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/scopes) clean up the newest objects first, this can happen when an old object depends on something much newer that itself. During cleanup, a situation could arise where the newer object is destroyed, then the older object runs code of some kind that needed the newer object to be there.

Fusion can check for situations like this by analysing the scopes. This message is shown when Fusion can prove one of these situations will occur.

There are two typical solutions:

  * If the objects should always be created and destroyed at the exact same time, then ensure they're created in the correct order.
  * Otherwise, move the objects into separate scopes, and ensure that both scopes can exist without the other scope.

## propertySetError¶

    Error setting property: UIAspectRatioConstraint.AspectRatio set to a
    non-positive value. Value must be a positive.

**Thrown by:** [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new), [`Hydrate`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/hydrate)

You attempted to set a property, but Roblox threw an error in response.

The error includes a more specific message which can be used to diagnose the issue.

## scopeMissing¶

    To create Observers, provide a scope. (e.g. `myScope:Observer(watching)`). See
    discussion #292 on GitHub for advice.

**Thrown by:** [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new), [`Hydrate`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/hydrate), [`Value`](https://elttob.uk/Fusion/0.3/api-reference/state/members/value), [`Computed`](https://elttob.uk/Fusion/0.3/api-reference/state/members/computed), [`Observer`](https://elttob.uk/Fusion/0.3/api-reference/state/members/observer), [`ForKeys`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forkeys), [`ForValues`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forvalues), [`ForPairs`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forpairs), [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/spring), [`Tween`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/tween)

**Related discussions:** [`#292`](https://github.com/dphfox/Fusion/discussions/292)

You attempted to create an object without providing a [scope](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/scopes) as the first parameter.

Scopes are mandatory for all Fusion constructors so that Fusion knows when the object should be destroyed.

## springNanGoal¶

    A spring was given a NaN goal, so some simulation has been skipped. Ensure no
    springs have NaN goals.

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/spring)

The goal parameter given to the spring during construction contained one or more NaN values.

This typically occurs when zero is accidentally divided by zero, or some other invalid mathematical operation has occurred. Check that your code is free of maths errors, and handles all edge cases.

## springNanMotion¶

    A spring encountered NaN during motion, so has snapped to the goal position.
    Ensure no springs have NaN positions or velocities.

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/spring)

While calculating updated position and velocity, one or both of those values ended up as NaN.

This typically occurs when zero is accidentally divided by zero, or some other invalid mathematical operation has occurred. Check that your code is free of maths errors, and handles all edge cases.

## springTypeMismatch¶

    The type 'Vector3' doesn't match the spring's type 'Color3'.

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/spring)

The spring expected you to provide a type matching the data type that the spring is currently outputting. However, you provided a different data type.

## stateGetWasRemoved¶

    `StateObject:get()` has been replaced by `use()` and `peek()` - see discussion
    #217 on GitHub.

**Thrown by:** [`Value`](https://elttob.uk/Fusion/0.3/api-reference/state/members/value), [`Computed`](https://elttob.uk/Fusion/0.3/api-reference/state/members/computed), [`ForKeys`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forkeys), [`ForValues`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forvalues), [`ForPairs`](https://elttob.uk/Fusion/0.3/api-reference/state/members/forpairs), [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/spring), [`Tween`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/tween)

**Related discussions:** [`#217`](https://github.com/dphfox/Fusion/discussions/217)

Older versions of Fusion let you call `:get()` directly on state objects to read their current value and attempt to infer dependencies.

This has been replaced by [use functions](https://elttob.uk/Fusion/0.3/api-reference/state/types/use) in Fusion 0.3 for more predictable behaviour and better support for constant values.

## tweenNanGoal¶

    A tween was given a NaN goal, so some animation has been skipped. Ensure no
    tweens have NaN goals.

**Thrown by:** [`Tween`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/tween)

The goal parameter given to the tween during construction contained one or more NaN values.

This typically occurs when zero is accidentally divided by zero, or some other invalid mathematical operation has occurred. Check that your code is free of maths errors, and handles all edge cases.

## tweenNanMotion¶

    A tween encountered NaN during motion, so has snapped to the goal. Ensure no
    tweens have NaN in their tween infos.

**Thrown by:** [`Tween`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/tween)

While calculating an updated tween position, the final value contained one or more NaN values.

This typically occurs when zero is accidentally divided by zero, or some other invalid mathematical operation has occurred. Check that your code is free of maths errors, and handles all edge cases.

## unknownMessage¶

    Unknown error: attempt to call a nil value

Fusion ran into a problem, but couldn't associate it with a valid type of error. This is a fallback error type which shouldn't be seen by end users, because it indicates that Fusion code isn't reporting errors correctly.

## unrecognisedChildType¶

    'string' type children aren't accepted by `[Children]`.

**Thrown by:** [`Children`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/children)

You provided a value inside of `[Children]` which didn't meet the definition of a [child](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/child) value. Check that you're only passing instances, arrays and state objects.

## unrecognisedPropertyKey¶

    'number' keys aren't accepted in property tables.

**Thrown by:** [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new), [`Hydrate`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/hydrate)

You provided something other than a property assignment (`Property = Value`) or [special key](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) in your property table.

Most commonly, this means you tried to add child instances directly into the property table, rather than passing them into the [`[Children]`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/children) special key.

## unrecognisedPropertyStage¶

    'children' isn't a valid stage for a special key to be applied at.

**Thrown by:** [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new), [`Hydrate`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/hydrate)

You attempted to use a [special key](https://elttob.uk/Fusion/0.3/api-reference/roblox/types/specialkey) which has a misconfigured `stage`, so Fusion didn't know when to apply it during instance construction.

## useAfterDestroy¶

    The Value object is no longer valid - it was destroyed before the Computed that
    is use()-ing. See discussion #292 on GitHub for advice.

**Thrown by:** [`Spring`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/spring), [`Tween`](https://elttob.uk/Fusion/0.3/api-reference/animation/members/tween), [`New`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/new), [`Hydrate`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/hydrate), [`Attribute`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/attribute), [`AttributeOut`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/attributeout), [`Out`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/out), [`Ref`](https://elttob.uk/Fusion/0.3/api-reference/roblox/members/ref), [`Computed`](https://elttob.uk/Fusion/0.3/api-reference/state/members/computed), [`Observer`](https://elttob.uk/Fusion/0.3/api-reference/state/members/observer)

**Related discussions:** [`#292`](https://github.com/dphfox/Fusion/discussions/292)

Your code attempted to access an object after that object was destroyed..

Make sure your objects are being added to the correct scopes according to when you expect them to be destroyed. Additionally, make sure your code can detect and deal with situations where other objects are no longer available.

Back to top


# Computeds

Computeds are state objects that immediately process values from other state objects.

You pass in a callback to define a calculation. Then, you can use `peek()` to read the result of the calculation at any time.

    local numCoins = scope:Value(50)
    local itemPrice = scope:Value(10)

    local finalCoins = scope:Computed(function(use, scope)
        return use(numCoins) - use(itemPrice)
    end)

    print(peek(finalCoins)) --> 40

    numCoins:set(25)
    itemPrice:set(15)
    print(peek(finalCoins)) --> 10

## Usage¶

To create a new computed object, call `scope:Computed()` and give it a function that performs your calculation. It takes two parameters which will be explained later; for the first part of this tutorial, they'll be left unnamed.

    6
    7
    8
    9

    local scope = scoped(Fusion)
    local hardMaths = scope:Computed(function(_, _)
        return 1 + 1
    end)

The value the callback returns will be stored as the computed's value. You can get the computed's current value using `peek()`:

     6
     7
     8
     9
    10
    11

    local scope = scoped(Fusion)
    local hardMaths = scope:Computed(function(_, _)
        return 1 + 1
    end)

    print(peek(hardMaths)) --> 2

The calculation should be _immediate_ \- that is, it should never delay. That means you should not use computed objects when you need to wait for something to occur (e.g. waiting for a server to respond to a request).

## Using State Objects¶

The calculation is only run once by default. If you try to `peek()` at state objects inside the calculation, your code breaks quickly:

     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16

    local scope = scoped(Fusion)
    local number = scope:Value(2)
    local double = scope:Computed(function(_, _)
        return peek(number) * 2
    end)

    print(peek(number), peek(double)) --> 2 4

    -- The calculation won't re-run! Oh no!
    number:set(10)
    print(peek(number), peek(double)) --> 10 4

Instead, the computed object provides a `use` function as the first argument. As your logic runs, you can call this function with different state objects. If any of them changes, then the computed throws everything away and recalculates.

     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17

    local scope = scoped(Fusion)
    local number = scope:Value(2)
    local double = scope:Computed(function(use, _)
        use(number) -- the calculation will re-run when `number` changes value
        return peek(number) * 2
    end)

    print(peek(number), peek(double)) --> 2 4

    -- Now it re-runs!
    number:set(10)
    print(peek(number), peek(double)) --> 10 20

For convenience, `use()` will also read the value, just like `peek()`, so you can easily replace `peek()` calls with `use()` calls. This keeps your logic concise, readable and easily copyable.

     6
     7
     8
     9
    10
    11
    12
    13
    14
    15

    local scope = scoped(Fusion)
    local number = scope:Value(2)
    local double = scope:Computed(function(use, _)
        return use(number) * 2
    end)

    print(peek(number), peek(double)) --> 2 4

    number:set(10)
    print(peek(number), peek(double)) --> 10 20

It's recommended you always give the first parameter the name `use`, even if it already exists. This helps prevent you from using the wrong parameter if you have multiple computed objects at the same time.

    scope:Computed(function(use, _)
        -- ...
        scope:Computed(function(use, _)
            -- ...
            scope:Computed(function(use, _)
                return use(number) * 2
            end)
            -- ...
        end)
        -- ...
    end)

Help! Using the same name gives me a warning.

Depending on your setup, Luau might be configured to warn when you use the same variable name multiple times.

In many cases, using the same variable name can be a mistake, but in this case we actually find it useful. So, to turn off the warning, try adding `--!nolint LocalShadow` to the top of your file.

Keep in mind that Fusion sometimes applies optimisations; recalculations might be postponed or cancelled if the value of the computed isn't being used. This is why you should not use computed objects for things like playing sound effects.

[You will learn more about how Fusion does this later.](https://elttob.uk/Fusion/0.3/tutorials/best-practices/optimisation/#similarity)

## Inner Scopes¶

Sometimes, you'll need to create things inside computed objects temporarily. In these cases, you want the temporary things to be destroyed when you're done.

You might try and reuse the scope you already have, to construct objects and add cleanup tasks.

Luau codeOutput

     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23

    local scope = scoped(Fusion)
    local number = scope:Value(5)
    local double = scope:Computed(function(use, _)
        local current = use(number)
        print("Creating", current)
        -- suppose we want to run some cleanup code for stuff in here
        table.insert(scope, function()
            print("Destroying", current)
        end)
        return current * 2
    end)

    print("...setting to 25...")
    number:set(25)
    print("...setting to 2...")
    number:set(2)
    print("...cleaning up...")
    doCleanup(scope)

    Creating 5
    ...setting to 25...
    Creating 25
    ...setting to 2...
    Creating 2
    ...cleaning up...
    Destroying 2
    Destroying 25
    Destroying 5

However, this doesn't work the way you'd want it to. All of the tasks pile up at the end of the program, instead of being thrown away with the rest of the calculation.

That's why the second argument is a different scope for you to use while inside the computed object. This scope argument is automatically cleaned up for you when the computed object recalculates.

Luau codeOutput

     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22

    local scope = scoped(Fusion)
    local number = scope:Value(5)
    local double = scope:Computed(function(use, myBrandNewScope)
        local current = use(number)
        print("Creating", current)
        table.insert(myBrandNewScope, function()
            print("Destroying", current)
        end)
        return current * 2
    end)

    print("...setting to 25...")
    number:set(25)
    print("...setting to 2...")
    number:set(2)
    print("...cleaning up...")
    doCleanup(scope)

    Creating 5
    ...setting to 25...
    Creating 25
    Destroying 5
    ...setting to 2...
    Creating 2
    Destroying 25
    ...cleaning up...
    Destroying 2

When using this new 'inner' scope, the tasks no longer pile up at the end of the program. Instead, they're cleaned up as soon as possible, when the computed object throws away the old calculation.

It can help to give this parameter the same name as the original scope. This stops you from accidentally using the original scope inside the computed, and makes your code more easily copyable and movable.

    local scope = scoped(Fusion)
    scope:Computed(function(use, scope)
        -- ...
        scope:Computed(function(use, scope)
            -- ...
            scope:Computed(function(use, scope)
                local innerValue = scope:Value(5)
            end)
            -- ...
        end)
        -- ...
    end)

Help! Using the same name gives me a warning.

Depending on your setup, Luau might be configured to warn when you use the same variable name multiple times.

In many cases, using the same variable name can be a mistake, but in this case we actually find it useful. So, to turn off the warning, try adding `--!nolint LocalShadow` to the top of your file.

Once you understand computeds, as well as the previously discussed scopes, values and observers, you're well positioned to explore the rest of Fusion.

Back to top


# Change Events

`OnChange` is a function that returns keys to use when hydrating or creating an instance. Those keys let you connect functions to property changed events on the instance.

    local input = scope:New "TextBox" {
        [OnChange "Text"] = function(newText)
            print("You typed:", newText)
        end

## Usage¶

`OnChange` doesn't need a scope - import it into your code from Fusion directly.

    local OnChange = Fusion.OnChange

When you call `OnChange` with a property name, it will return a special key:

    local key = OnChange("Text")

When used in a property table, you can pass in a handler and it will be run when that property changes.

Arguments are different to Roblox API

Normally in the Roblox API, when using `:GetPropertyChangedSignal()` on an instance, the callback will not receive any arguments.

To make working with change events easier, `OnChange` will pass the new value of the property to the callback.

    local input = scope:New "TextBox" {
        [OnChange("Text")] = function(newText)
            print("You typed:", newText)
        end

If you're using quotes `'' ""` for the event name, the extra parentheses `()` are optional:

    local input = scope:New "TextBox" {
        [OnChange "Text"] = function(newText)
            print("You typed:", newText)
        end

Back to top


#  Spring ¶

    export type Spring<T> = StateObject<T> & {
        kind: "Spring",
        setPosition: (self, newPosition: T) -> (),
        setVelocity: (self, newVelocity: T) -> (),
        addVelocity: (self, deltaVelocity: T) -> ()

A specialised [state object](https://elttob.uk/Fusion/0.3/api-reference/animation/types/stateobject) for following a goal state smoothly over time, using physics to shape the motion.

The methods on this type allow for direct control over the position and velocity of the motion. Other than that, this type is of limited utility outside of Fusion itself.

## Members¶

### kind  : "Spring"  ¶

A more specific type string which can be used for runtime type checking. This can be used to tell types of state object apart.

## Methods¶

### setPosition  -> ()  ¶

    function Spring:setPosition(
        newPosition: T
    ): ()

Immediately snaps the spring to the given position. The position must have the same `typeof()` as the goal state.

### setVelocity  -> ()  ¶

    function Spring:setVelocity(
        newVelocity: T
    ): ()

Overwrites the spring's velocity without changing its position. The velocity must have the same `typeof()` as the goal state.

### addVelocity  -> ()  ¶

    function Spring:addVelocity(
        deltaVelocity: T
    ): ()

Appends to the spring's velocity without changing its position. The velocity must have the same `typeof()` as the goal state.

## Learn More¶

  * [Springs tutorial](https://elttob.uk/Fusion/0.3/tutorials/animation/springs)

Back to top


#  UsedAs ¶

    export type UsedAs<T> = T | StateObject<T>

Something which describes a value of type `T`. When it is [used](https://elttob.uk/Fusion/0.3/api-reference/state/types/use) in a calculation, it becomes that value.

Recommended

Instead of using one of the more specific variants, your code should aim to use this type as often as possible. It allows your logic to deal with many representations of values at once,

## Variants¶

  * `T` \- represents unchanging constant values
  * [`StateObject<T>`](https://elttob.uk/Fusion/0.3/api-reference/state/types/stateobject) \- represents dynamically updating values

## Learn More¶

  * [Components tutorial](https://elttob.uk/Fusion/0.3/tutorials/best-practices/components/)

Back to top


#  Spring -> [Spring](https://elttob.uk/Fusion/0.3/api-reference/animation/types/spring)<T> ¶

    function Fusion.Spring<T>(
        scope: Scope<unknown>,
        goal: UsedAs<T>,
        speed: UsedAs<number>?,
        damping: UsedAs<number>?
    ) -> Spring<T>

Constructs and returns a new [spring state object](https://elttob.uk/Fusion/0.3/api-reference/animation/types/spring).

Use scoped() method syntax

This function is intended to be accessed as a method on a scope:

    local spring = scope:Spring(goal, speed, damping)

## Parameters¶

### scope  : [Scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope)<S> ¶

The [scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope) which should be used to store destruction tasks for this object.

### goal  : [UsedAs](https://elttob.uk/Fusion/0.3/api-reference/state/types/used)<T> ¶

The goal that this object should follow. For best results, the goal should be [animatable](https://elttob.uk/Fusion/0.3/api-reference/animation/types/animatable).

### speed  : [UsedAs](https://elttob.uk/Fusion/0.3/api-reference/state/types/usedas)<T>?  ¶

Multiplies how fast the motion should occur; doubling the `speed` exactly halves the time it takes for the motion to complete.

### damping  : [UsedAs](https://elttob.uk/Fusion/0.3/api-reference/state/types/usedas)<T>?  ¶

The amount of resistance the motion encounters. 0 represents no resistance, 1 is just enough resistance to prevent overshoot (critical damping), and larger values damp out inertia effects and straighten the motion.

## Returns  -> [Spring](https://elttob.uk/Fusion/0.3/api-reference/animation/types/spring)<T> ¶

A freshly constructed spring state object.

## Learn More¶

  * [Springs tutorial](https://elttob.uk/Fusion/0.3/tutorials/animation/springs)

Back to top


#  ForPairs -> [For](https://elttob.uk/Fusion/0.3/api-reference/state/types/for)<KO, VO> ¶

    function Fusion.ForPairs<KI, KO, VI, VO, S>(
        scope: Scope<S>,
        inputTable: UsedAs<{[KI]: VI}>,
        processor: (Use, Scope<S>, key: KI, value: VI) -> (KO, VO)
    ) -> For<KO, VO>

Constructs and returns a new [For state object](https://elttob.uk/Fusion/0.3/api-reference/state/types/for) which processes keys and values in pairs.

Use scoped() method syntax

This function is intended to be accessed as a method on a scope:

    local forObj = scope:ForPairs(inputTable, processor)

## Parameters¶

### scope  : [Scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope)<S> ¶

The [scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope) which should be used to store destruction tasks for this object.

### inputTable  : [UsedAs](https://elttob.uk/Fusion/0.3/api-reference/state/types/usedas)<{[KI]: VI}> ¶

The table which will provide the input keys and input values for this object.

If it is a state object, this object will respond to changes in that state.

### processor  : ([Use](https://elttob.uk/Fusion/0.3/api-reference/memory/types/use), [Scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope)<S>, key: KI, value: VI) -> (KO, VO)  ¶

Accepts a `KI` key and `VI` value pair from the input table, and returns the `KO` key and `VO` value pair that should appear in the output table.

The processor is given a [use function](https://elttob.uk/Fusion/0.3/api-reference/memory/types/use) for including other objects in the computation, and a [scope](https://elttob.uk/Fusion/0.3/api-reference/memory/types/scope) for queueing destruction tasks to run on re-computation. The given scope has the same methods as the scope used to create the whole object.

## Returns  -> [For](https://elttob.uk/Fusion/0.3/api-reference/state/types/for)<KO, VO> ¶

A freshly constructed For state object.

## Learn More¶

  * [ForPairs tutorial](https://elttob.uk/Fusion/0.3/tutorials/tables/forpairs)

Back to top


#  Version ¶

    export type Version = {
        major: number,
        minor: number,
        isRelease: boolean

Describes a version of Fusion's source code.

## Members¶

### major  : number  ¶

The major version number. If this is greater than `0`, then two versions sharing the same major version number are not expected to be incompatible or have breaking changes.

### minor  : number  ¶

The minor version number. Describes version updates that are not enumerated by the major version number, such as versions prior to 1.0, or versions which are non-breaking.

### isRelease  : boolean  ¶

Describes whether the version was sourced from an official release package.

Back to top


# Player List

This shows how to use Fusion's Roblox API to create a simple, dynamically updating player list.

## Overview¶

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79

    local Players = game:GetService("Players")

    local Fusion = -- initialise Fusion here however you please!
    local scoped = Fusion.scoped
    local Children = Fusion.Children
    type UsedAs<T> = Fusion.UsedAs<T>

    local function PlayerList(
        scope: Fusion.Scope,
        props: {
            Players: UsedAs<{Player}>
    ): Fusion.Child
        return scope:New "Frame" {
            Name = "PlayerList",

            Position = UDim2.fromScale(1, 0),
            AnchorPoint = Vector2.new(1, 0),
            Size = UDim2.fromOffset(300, 0),
            AutomaticSize = "Y",

            BackgroundTransparency = 0.5,
            BackgroundColor3 = Color3.new(0, 0, 0),

            [Children] = {
                scope:New "UICorner" {
                    CornerRadius = UDim.new(0, 8)
                },
                scope:New "UIListLayout" {
                    SortOrder = "Name",
                    FillDirection = "Vertical"
                },

                scope:ForValues(props.Players, function(use, scope, player)
                    return scope:New "TextLabel" {
                        Name = "PlayerListRow: " .. player.DisplayName,

                        Size = UDim2.new(1, 0, 0, 25),
                        BackgroundTransparency = 1,

                        Text = player.DisplayName,
                        TextColor3 = Color3.new(1, 1, 1),
                        Font = Enum.Font.GothamMedium,
                        TextSize = 16,
                        TextXAlignment = "Right",
                        TextTruncate = "AtEnd",

                        [Children] = scope:New "UIPadding" {
                            PaddingLeft = UDim.new(0, 10),
                            PaddingRight = UDim.new(0, 10)
                end)
    end

    -- Don't forget to pass this to `doCleanup` if you disable the script.
    local scope = scoped(Fusion, {
        PlayerList = PlayerList
    })

    local players = scope:Value(Players:GetPlayers())
    local function updatePlayers()
        players:set(Players:GetPlayers())
    end
    table.insert(scope, {
        Players.PlayerAdded:Connect(updatePlayers),
        Players.PlayerRemoving:Connect(updatePlayers)
    })

    local gui = scope:New "ScreenGui" {
        Name = "PlayerListGui",
        Parent = Players.LocalPlayer:FindFirstChildOfClass("PlayerGui"),

        [Children] = scope:PlayerList {
            Players = players

## Explanation¶

The `PlayerList` component is designed to be simple and self-contained. The only thing it needs is a `Players` list - it handles everything else, including its position, size, appearance and behaviour.

    local function PlayerList(
        scope: Fusion.Scope,
        props: {
            Players: UsedAs<{Player}>
    ): Fusion.Child

After creating a vertically expanding Frame with some style and layout added, it turns the `Players` into a series of text labels using `ForValues`, which will automatically create and remove them as the `Players` list changes.

                scope:ForValues(props.Players, function(use, scope, player)
                    return scope:New "TextLabel" {
                        Name = "PlayerListRow: " .. player.DisplayName,

                        Size = UDim2.new(1, 0, 0, 25),
                        BackgroundTransparency = 1,

                        Text = player.DisplayName,
                        TextColor3 = Color3.new(1, 1, 1),
                        Font = Enum.Font.GothamMedium,
                        TextSize = 16,
                        TextXAlignment = "Right",
                        TextTruncate = "AtEnd",

                        [Children] = scope:New "UIPadding" {
                            PaddingLeft = UDim.new(0, 10),
                            PaddingRight = UDim.new(0, 10)
                end)

That's all that the `PlayerList` component has to do.

Later on, the code creates a `Value` object to store a list of players, and update it every time a player joins or leaves the game.

    local players = scope:Value(Players:GetPlayers())
    local function updatePlayers()
        players:set(Players:GetPlayers())
    end
    table.insert(scope, {
        Players.PlayerAdded:Connect(updatePlayers),
        Players.PlayerRemoving:Connect(updatePlayers)
    })

That object can then be passed in as `Players` when creating the `PlayerList`.

    local gui = scope:New "ScreenGui" {
        Name = "PlayerListGui",
        Parent = Players.LocalPlayer:FindFirstChildOfClass("PlayerGui"),

        [Children] = scope:PlayerList {
            Players = players

Back to top


#  Use ¶

    export type Use = <T>(target: UsedAs<T>) -> T

A function which extracts a value of `T` from something that can be [used as](https://elttob.uk/Fusion/0.3/api-reference/state/types/usedas) `T`.

The most generic implementation of this is [the `peek()` function](https://elttob.uk/Fusion/0.3/api-reference/state/members/peek), which performs this extraction with no additional steps.

However, certain APIs may provide their own implementation, so they can perform additional processing for certain representations. Most notably, [computeds](https://elttob.uk/Fusion/0.3/api-reference/state/members/computed) provide their own `use()` function which adds inputs to a watchlist, which allows them to re-calculate as inputs change.

## Parameters¶

### target  : [UsedAs](https://elttob.uk/Fusion/0.3/api-reference/state/types/usedas)<T> ¶

The representation of `T` to extract a value from.

## Returns  -> T  ¶

The current value of `T`, derived from `target`.

## Learn More¶

  * [Values tutorial](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/values/)
  * [Computeds tutorial](https://elttob.uk/Fusion/0.3/tutorials/fundamentals/computeds/)

Back to top