Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limiting state writing #110

Open
dphfox opened this issue Dec 28, 2021 · 4 comments
Open

Limiting state writing #110

dphfox opened this issue Dec 28, 2021 · 4 comments
Labels
enhancement New feature or request not ready - evaluating Currently gauging feedback

Comments

@dphfox
Copy link
Owner

dphfox commented Dec 28, 2021

Currently, set and get operations are present as methods on a single state object:

local value = Value(5)

value:set(15)
print(value:get()) -- 15

However, this poses a problem: what if we don't want to give full write permissions to someone we're giving our state objects to?

local function makeReadOnly()
    local readOnly = Value(0)
    task.defer(function()
        repeat
            readOnly:set(readOnly:get() + 1)
        until false
    end)
    return readOnly
end

local readOnly = makeReadOnly()

print(readOnly:get()) -- ok
readOnly:set(9001) -- oh no - the contract is not enforced

Users end up having to get inventive with weird solutions like passthrough Computed objects, which aren't really great:

local function makeReadOnly()
    local readOnly = Value(0)
    task.defer(function()
        repeat
            readOnly:set(readOnly:get() + 1)
        until false
    end)
    -- this would seem unnecessary to a casual reader
    return Computed(function()
        return readOnly:get()
    end)
end

local readOnly = makeReadOnly()

print(readOnly:get()) -- ok
readOnly:set(9001) -- errors - that's better :)

It might be worth investigating some way of separating out the reading and writing abilities of a state object.
One solution could be adopting React/Solid-like setter/getter splitting (open question: how does this translate to objects with more complex setting behaviour, for example a spring where we might want to read and write aspects like velocity and position?)

local function doLimited(getX)
    -- can't change the value of x here, can only read from it
    print("x is", getX())
end

local getX, setX = Value(10)

doLimited(getX) -- the value of x is 10
setX(25)
doLimited(getX) -- the value of x is 25

Another solution, perhaps more Fusion-like, would be introducing a utility ReadOnly function that creates a limited version of a state object for the user - this would almost certainly be optimised under the hood:

local function doLimited(x)
    -- can't change the value of x here, can only read from it
    print("x is", x:get())
end

local myValue = Value(10)
local limitedValue = ReadOnly(myValue)

doLimited(limitedValue) -- x is 10
myValue:set(25)
doLimited(limitedValue) -- x is 25
@dphfox
Copy link
Owner Author

dphfox commented Dec 28, 2021

It's worth mentioning that a solution here could possibly relate partially to #35, as changing how we retrieve values could potentially also change how we treat constants versus state.

@dphfox dphfox added enhancement New feature or request not ready - evaluating Currently gauging feedback labels Dec 28, 2021
@dphfox dphfox added this to Fusion 0.3 Feb 1, 2023
@dphfox dphfox moved this to Prospective in Fusion 0.3 Feb 1, 2023
@dphfox
Copy link
Owner Author

dphfox commented Aug 22, 2023

Another idea I like, though it doesn't address all the motivating problems, is the ability for value objects to give away write control over their value, turning the object itself read-only:

local foo = Value(5)

local access = foo:giveMeExclusiveWriteControl()

access:set(10)
print(peek(foo)) -- 10
foo:set(2) -- error

@Dionysusnu
Copy link
Contributor

I think it makes more sense to do that as local foo, access = ReadOnly().
One-time operations on a State don't fit well in the reactive graph, and I can't imagine many cases where you wouldn't want to do it as one of the first things when a Value is created anyways.

@dphfox
Copy link
Owner Author

dphfox commented Aug 23, 2023

I think it makes more sense to do that as local foo, access = ReadOnly().
One-time operations on a State don't fit well in the reactive graph, and I can't imagine many cases where you wouldn't want to do it as one of the first things when a Value is created anyways.

Mostly I'm thinking of use cases where you pass a value into a function call with the expectation that the function call starts maintaining that value for you, in which case it becomes a way of enforcing strongly that two agents don't have write access at the same time. Think e.g. [Ref].

@dphfox dphfox removed this from Fusion 0.3 Jul 3, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request not ready - evaluating Currently gauging feedback
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants