goldenstein64 / Copy

A Luau-specialized module for copying any value with state.
https://goldenstein64.github.io/Copy
MIT License
0 stars 0 forks source link

Context prioritizes over Transform #5

Closed goldenstein64 closed 3 years ago

goldenstein64 commented 4 years ago

This branch changes modifier precedence so that a new property, Copy.Context, is prioritized over Copy.Transform. This allows for much more specific changes in the table based on its shape.

Given a table like so:

local someTable = {
  sub = {
    key = "a string"
  },
  key = "a string"
}

Whereas the workflow for replacing values in sub-tables would usually look like this:

Copy.Transform["a string"] = "new string"
Copy.Transform[someTable.sub] = Copy(someTable.sub)
-- above Copy() call flushes out the ["a string"] = "new string" transformation
local newTable = Copy(someTable)

The behavior can now be clearly shown by creating a table in Copy.Context in the shape of someTable, with someTable.sub.key being transformed in this example:

Copy.Context = {
  sub = {
    key = Copy:repl{ value = "new string" }
  }
}
local newTable = Copy(someTable)

Although it takes more lines to write, it's also much, much harder to get wrong.

This new property also introduces a new function, Copy:repl, which is meant to be used solely in setting the Context property. This process is more streamlined in Copy:ApplyContext, which takes a function as an argument, with Copy:repl passed in as a parameter to said argument function:

Copy:ApplyContext(function(replace) return {
    sub = {
      key = replace{ value = "new string" }
    }
} end)
local newTable = Copy(someTable)
goldenstein64 commented 4 years ago

As for what these functions do in the back-end, Copy:Replace returns the table passed in, with Copy.Tag assigned as a key so that it can be recognized as a representation of replacing a key or value.

All Copy:ApplyContext does is create a function that calls Copy:Replace and then call the parameter function with the new replace function passed in. I find this to be very ugly due to the amount of seemingly unnecessary boilerplate it creates; I would rather find a way to make Copy:Replace shorter or easier to write and resort to a direct Copy.Context = ... assignment.

I renamed Copy:Replace to Copy:repl, which works alright I guess, I can't really tell.

goldenstein64 commented 4 years ago

Finally, I think Copy.Context should extend its control over to values that are being preserved or copied, but then this would leave the realm of copying values and become transforming them.

e.g:

local someTable = {
  sub = {
    key = "a string"
  }
}

Copy:QueuePreserve(someTable.sub)
Copy.Context = {
  sub = {
    key = Copy:repl{ value = "new string" }
  }
}
local newTable = Copy(someTable)

assert(newTable.sub.key == "new string")

This could even expand to Copy.Context creating new values in empty fields:

local someTable = {
  key = "a string"
}

Copy.Context = {
  newKey = Copy:repl{ value = "new string" }
}
local newTable = Copy(someTable)

assert(newTable.key == "a string")
assert(newTable.newKey == "new string")

Note: This might actually be needed for "transforming" fields with nil values, since they also double as empty fields that aren't looped through.

Maybe Copy.Context could be built without Copy:repl for new fields:

Copy.Context = {
  newKey = "new string"
}

If this were implemented, it would be very similar to using Copy:Extend. It would probably be better to keep Copy.Context for replacing specific values and Copy:Extend for more template-like behavior.

Maybe I could convert this project into a "Table Distorter" utility that helps devs create or modify tables so that they can shape them to their liking...