Skip to content
This repository has been archived by the owner on Dec 13, 2023. It is now read-only.

WIP: Bindings #159

Merged
merged 44 commits into from
Oct 29, 2018
Merged

Conversation

ZoteTheMighty
Copy link
Contributor

@ZoteTheMighty ZoteTheMighty commented Oct 19, 2018

Closes #158

This PR is a work in progress and is built off of the new-reconciler branch

An initial implementation of Bindings. This addresses the basic use cases for it, and has some mechanisms to prevent unintended access to private functionality.

It also includes a very arbitrary example of how bindings can modify values outside of Roact's render in a more straightforward way than is possible with refs.

TODO:

  • Clean up any changes in unrelated files
  • Figure out plans for hiding private members of Bindings or not
  • Document Bindings feature
  • Add benchmarks, particularly for binding.map which may run every reconcile
  • Solicit/incorporate any feedback for Bindings API
  • Determine a mechanism to enable/disable the Bindings feature if needed

Copy link
Contributor

@LPGhatguy LPGhatguy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A handful of changes!

@@ -1,4 +1,4 @@
local PlayerGui = game:GetService("Players").LocalPlayer.PlayerGui
local PlayerGui = game:GetService("Players").LocalPlayer:WaitForChild("PlayerGui")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An unfortunate consequence of APS, I guess: all my synchronization bugs in all the example code I write start coming to light.

lib/Binding.lua Outdated Show resolved Hide resolved
lib/Binding.lua Outdated
Create a new binding object with the given starting value. This
function will be exposed to users of Roact.
]]
function Binding.create(initialValue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I've been doing lately is separating instance methods from static methods, since it becomes clearer which is which. Since we don't expose Binding to users, that's a little less important.

lib/Binding.lua Outdated
[Internal.changeSignal] = createSignal(),
}

binding[Internal.update] = function(self, newValue)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we define these once and then use metatables to get at them?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's probably the way to go. The thing that made this approach convenient is that I could use upvalues instead of making everything private via Symbols, which is an elegant solution conceptually but it makes the bodies of my functions kind of hideous.

lib/Binding.spec.lua Outdated Show resolved Hide resolved
lib/createRef.spec.lua Show resolved Hide resolved
]]

local DEBUG_SIGNAL = true
local debug_globalSubs = 0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we drop these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I intended to prune them from the final version

lib/init.lua Outdated

Change = require(script.PropMarkers.Change),
Children = require(script.PropMarkers.Children),
Event = require(script.PropMarkers.Event),
Ref = require(script.PropMarkers.Ref),

createBinding = Binding.create,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be grouped with the other methods!

rojo.json Outdated
"start-benchmarks": {
"path": "start-benchmarks.server.lua",
"target": "ServerScriptService.start-benchmarks"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When you're working locally, you can set the Disabled property on these scripts instead of removing them from your rojo.json config!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, but that's only if the file exists :P I think the current state of new-reconciler might be missing the benchmark runner file:
thread '<unnamed>' panicked at 'Unable to watch partition start-benchmarks, with path C:\Users\pdoyle\dev\projects\refs-sample-project\modules\roact\start-benchmarks.server.lua! Make sure that it's a file or directory.', src\vfs\vfs_watcher.rs:86:25 note: Run with RUST_BACKTRACE=1 for a backtrace.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to revert this for the time being in this PR? I'll figure out whatever fun merge mishap happened that made us lose some of these things.

lib/Binding.lua Outdated
Creates a new binding from the given source and with the given mapping.

The source can either be a starting value or another Binding object,
which is useful for mapping a binding onto another binding
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this function work only for creating a binding from another binding?

Alternatively, can we just inline this functionality into what map does?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this was where I was a bit conflicted trying to converge creating a binding vs. creating a mapped binding. I think moving the logic into map is probably reasonable.

lib/RobloxRenderer.spec.lua Show resolved Hide resolved
lib/createRef.spec.lua Show resolved Hide resolved
rojo.json Outdated
"start-benchmarks": {
"path": "start-benchmarks.server.lua",
"target": "ServerScriptService.start-benchmarks"
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you want to revert this for the time being in this PR? I'll figure out whatever fun merge mishap happened that made us lose some of these things.

lib/Binding.lua Outdated Show resolved Hide resolved
lib/Binding.lua Outdated
local binding = Binding.create(mapFunc(self:getValue()))

binding._mapFunc = mapFunc
binding._source = self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably use a more descriptive name, like __upstreamBinding?

lib/Binding.lua Outdated
This allows us to avoid subscribing to our source until someone
has subscribed to us, and avoid creating dangling connections
]]
if Type.of(self._source) == Type.Binding and self._disconnectSource == nil then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it sufficient to just check if self._source ~= nil here? It probably shouldn't be any other type!

lib/Binding.lua Outdated
_value = initialValue,
_changeSignal = createSignal(),
_mapFunc = mapIdentity,
_subCount = 0,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's list out other values we have here, even if they're nil, just so that we have a central place to look.

lib/Binding.lua Outdated
local binding = {
[Type] = Type.Binding,

_value = initialValue,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to group together all of the internal properties into a sub-table indexed by a symbol? I've been using this pattern in a couple places to hide values in a way that's a little more structured than this naming convention.

lib/Binding.lua Outdated
end

local Binding = {}
local BindingInternal = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rename this to prototype or bindingPrototype to make it more explicit what it does?

@LPGhatguy LPGhatguy merged commit 1bc1476 into Roblox:new-reconciler Oct 29, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants