goldenstein64 / copy Goto Github PK
View Code? Open in Web Editor NEWA Luau-specialized module for copying any value with state.
Home Page: https://goldenstein64.github.io/Copy
License: MIT License
A Luau-specialized module for copying any value with state.
Home Page: https://goldenstein64.github.io/Copy
License: MIT License
A version of Copy:BehaveAs
that allows for broader control over how values are copied. I would probably go for a shorter function name though, like Copy:Contextualize
(which isn't even shorter).
Given:
local value1 = {
key = "value 1"
}
local value2 = {
key = "value 2"
}
local value3 = {
key = "value 3"
}
local some = {
key1 = value1,
key2 = value2,
key3 = value3,
}
Instead of:
local base = {
key1 = Copy:BehaveAs("replace", value1),
key2 = Copy:BehaveAs("replace", value2),
key3 = Copy:BehaveAs("replace", value3),
}
Copy:Extend(some, base)
You can do:
local context = {
Values = "replace",
}
local base = Copy:CreateContext(context, some)
Copy:Extend(some, base)
In this example you can just do this, but it seems really hacky:
Copy.GlobalContext.Values = "replace"
Copy:Extend(some, some)
Copy.GlobalContext.Values = "default"
Test:
local value1 = { key = "value 1" }
local value2 = { key = "value 2" }
local value3 = { key = "value 3" }
it("can create contexts", function()
local some = {
key1 = value1,
key2 = value2,
key3 = value3,
}
local context = {
Values = "replace"
}
local base = Copy:CreateContext(context, some)
Copy:Extend(some, base)
for i, value in ipairs{value1, value2, value3} do
local usedKey = string.format("key%d", i)
local subValue = string.format("value %d", i)
local subSome = some[usedKey]
expect(subSome).to.be.a("table")
expect(subSome).to.never.equal(value)
expect(subSome.key).to.equal(subValue)
end
end)
A possibly more intuitive behavior could be making Copy:CreateContext
apply to the root value as well as its sub-values. I feel like that would be complicated though because there would be extra logic for what context the root value is currently in, and that could be confusing/unwanted behavior too.
This would only be useful for cases where transformers can directly affect sub-values. This is only true for "reconcile"
and "replace"
, so there aren't enough transformers for this to be applied to its full effect.
When to use Copy: clarify that functions can't be copied at the end.
API Reference: Describe what types each behavior supports
Clarify that Copy.Transform
accepts any value in a key-value mapping, and that it's not recommended to use them as keys.
"These functions can be found in the test
directory found here." -> "These functions can be found in the test
directory."
Symbol API: Turn Symbol()
into a function type, Symbol(T) -> (boolean, T)
.
Clarify that functions used as symbols can be called "symbol functions".
Reinforce that you have to register a symbol function to Copy.SymbolMap
before using it.
For the function parameters, "the old value's field" -> "the old value"
It might be more ergonomic to use metatables to describe how table sub-values are copied instead of Copy:CreateContext
or Copy:BehaveAs
. It would be exactly what a metatable is used for, it would have exactly the same functionality as Copy:BehaveAs
, it would stay connected to the normal table (or multiple tables), and it would stay out of the way very easily.
it("can use the '__copy' metatable field", function()
local sub = {
key = "some value"
}
local some = {
owned = sub,
shared = sub
}
local mt = {}
-- __copy represents a mapping of keys to behaviors/symbols for sub-values
-- omitted keys are treated as default
mt.__copy = {
shared = "set"
}
setmetatable(some, mt)
local new = Copy(some)
expect(new).to.never.equal(owned)
expect(new.shared).to.equal(some.shared)
expect(new.owned).to.never.equal(new.owned)
end)
It might also make classes easier to implement?
local someClass = {}
local prototype = {
owned = {},
shared = {},
key = "value"
}
function prototype:Method(name)
return self.owned[name] or self.shared[name]
end
local mt = {}
mt.__copy = {
shared = "set"
}
setmetatable(prototype, mt)
someClass.prototype = prototype
function someClass.new()
return Copy(someClass.prototype)
end
return someClass
Combining behaviors should be shared AFTER all the low level behaviors are introduced.
A table that gets flushed after a copy call is finished. We can then make Copy.Transform
persistent after every call and make TempTransform
use __index
on it.
it("has a persistent Transform", function()
Copy.Transform["value"] = "some other value"
Copy(nil)
expect(Copy.Transform["value"]).to.equal("some other value")
end)
it("has a temporary Transform", function()
Copy.TempTransform["value"] = "some other value"
Copy(nil)
expect(Copy.TempTransform["value"]).to.equal(nil)
end)
This would affect every file that uses Transform
, since we are changing the behavior of an existing namespace.
The only thing I can provide in the usage guide is use cases, not common properties of situations where Copy
would be a good solution. Although Copy
covers a novel use case, Lua has a programming pattern that overlaps with where Copy
is most extensible and useful: factory functions.
-- where Copy can do this,
local prototype = {
property = "value"
}
local obj = Copy(prototype)
-- factory functions can do it just as easily!
-- and arguments can be added
-- to change the table's configuration:
local function makeObj(...)
return {
property = "value",
...
}
end
local obj = makeObj(...)
A well-structured factory function will always be better than a well-structured application of Copy
because it's simpler and clearer. Even if the table is huge, you still have to write the factory function only once.
Maybe a place where Copy
would be better is when the prototype is programmatically created:
local prototype = {}
for _, child in ipairs(script:GetChildren()) do
prototype[child.Name] = child:GetAttribute("Value")
end
local obj = Copy(prototype)
-- vs
local function makeObj()
local obj = {}
for _, child in ipairs(script:GetChildren()) do
obj[child.Name] = child:GetAttribute("Value")
end
return obj
end
local obj = makeObj()
It would be more performance-intensive to use a factory function to copy each child's value than it would be to Copy()
it.
So far, my conclusion is that factory functions become as complex as the table it constructs. However, the complexity of Copy()
stays the same, and the responsible prototype carries all the complexity. Copy()
can be combined with factory functions to create a different pathway to the same result; maybe it's simpler to start from a copy and modify it instead of starting from an empty table or a table construction.
Possibly required infrastructure for #7
Acts as an internal state the way Instances.ApplyTransform
does. It would make some of the code simpler instead of passing behaviors, contexts, etc. and making the argument list too long.
Basically my excuse to use a structure similar to RaycastParams
If a given value isn't compatible with the given transformer, it can throw an error.
e.g.
local some = "value"
expect(function()
Copy:BehaveAs("replace", some)
end).to.throw()
Purely optional, a flag should be made for it.
If no transformer matches among multiple, it should throw an error as well.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.