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

require commands to artificially require devices or supplies #201

Closed
byorgey opened this issue Oct 8, 2021 · 11 comments · Fixed by #533
Closed

require commands to artificially require devices or supplies #201

byorgey opened this issue Oct 8, 2021 · 11 comments · Fixed by #533
Assignees
Labels
C-Low Hanging Fruit Ideal issue for new contributors. L-Capability checking Capability checking determines which capabilities are required by a given piece of code. S-Nice to have The bug fix or feature would be nice but doesn't currently have much negative impact. Z-Feature A new feature to be added to the game. Z-User Experience This issue seeks to make the game more enjoyable to play.

Comments

@byorgey
Copy link
Member

byorgey commented Oct 8, 2021

[Edited to add: I have realized that this issue is even more general, and is not specifically about devices per se, but about requiring any kind of needed supplies. See the comments later in the issue.]

In certain situations, it's possible you might want a robot to have a certain device installed even though its program does not require the capabilities provided by that device. For example, this can happen with loggers: if the robot might crash and you want it to be able to log its failure. Or you might be planning to reprogram the robot later and know the later program will require a certain capability that it does not require at first. We should add a command require which artificially creates a demand for a specific device, so the build command will install it. For example,

build "x" {require "logger"; move; grab; move}

will build a robot x with a logger installed.

Of course require is a no-op at runtime.

I think there is actually a question here whether the argument to require should name a capability or a device. In general I think we need to clarify the relationship between capabilities and devices.

@byorgey byorgey added Z-User Experience This issue seeks to make the game more enjoyable to play. Z-Feature A new feature to be added to the game. C-Low Hanging Fruit Ideal issue for new contributors. S-Nice to have The bug fix or feature would be nice but doesn't currently have much negative impact. L-Capability checking Capability checking determines which capabilities are required by a given piece of code. labels Oct 8, 2021
@byorgey
Copy link
Member Author

byorgey commented Oct 8, 2021

The boat is another example of a device that might be needed but not by a specific command.

@xsebek
Copy link
Member

xsebek commented Oct 8, 2021

Could users not define it themselves (or copy from the tutorial) using install after the build?

def build_with = \name.\program.\needs\.
  x <- build name program;
  install x needs;
  return x;
end

I usually define robots for scouting/fetching entities, so the name and necessary devices are always the same and I only change the program. Is there a use case for require that a good tutorial (and load #7) would not solve better? 🤔

@byorgey
Copy link
Member Author

byorgey commented Oct 8, 2021

Yes, build_with works up to a point, but:

  1. If you need multiple devices, you would have to make a version of build_with that takes a list, or make build_with2 and build_with3...
  2. It is easy to get wrong. In fact I think your build_with may have a subtle race condition: depending on the order in which the robots execute and what command program executes first, the newly built robot may drive off before the install command is complete. It would be safer to say x <- build name {wait 1; program}.
  3. It is not compositional. In all other cases, what devices a program needs is determined simply by what commands it uses. But in the case of a few devices like boat and logger, you must globally remember to build them with the right invocation of build_with. Imagine that you were designing a robot to carry out some complex task which you broke down into pieces. One of those pieces involves crossing a lake. You want to be able to say require "boat" right in the definition of that piece; the property of requiring a boat is thus expressed as a local property of that definition, and will compositionally carry over to any other bigger definitions which reuse that one.

I am all for having users define things themselves, but in this case I don't think they have the tools to actually build the abstraction they would really want.

@xsebek
Copy link
Member

xsebek commented Oct 8, 2021

Thanks for the added clarification @byorgey, now I sorely miss require in the game too 😄

@byorgey byorgey assigned byorgey and unassigned byorgey Oct 13, 2021
@byorgey byorgey self-assigned this Mar 18, 2022
@byorgey
Copy link
Member Author

byorgey commented Mar 18, 2022

Tried starting on implementing this (in the require branch) and it seems to be a bit trickier than I thought, so writing down what I've learned so far and some current thoughts.

  • I feel pretty strongly that the require command should take the name of a device as an argument, since that is what users know about. Users shouldn't need to know or care about the capability system.
  • Currently, we figure out capability requirements by calling the requiredCaps function, which returns a set of capabilities. In order to properly handle require, we would need to:
    • Have a special case for saturated applications of require to a literal string. We can't say what capabilities the require constant requires on its own. Perhaps this suggests that require should be its own special syntactic form?
    • Have access to a mapping from device names to capabilities, in order to translate the name of a device (provided as an argument to require) into a set of capabilities suitable for returning. This is tricky/annoying since it would require plumbing an EntityMap through the requiredCaps function in order to be able to look up devices by name.
  • In any case, it seems weird/redundant to turn a device name into a set of capabilities just so we can then turn right around and figure out what device would provide those capabilities.
  • Another possibility is to change the return type of requiredCaps so that it simply returns a set of capabilities OR device names, i.e. a Set (Either Capability Text). This would avoid having to round-trip through a set of capabilities, and pushes off the work of looking up devices by name to the caller of requiredCaps.
    • Note that this would require updating the definition of CapCtx as well, since definitions can have require in them, and we need to remember what devices they require.
    • More generally, we might consider defining something like data Requirement = Device Text | Cap Capability and then having everything deal with Set Requirement instead of Set Capability.

@byorgey byorgey removed their assignment Mar 19, 2022
byorgey added a commit that referenced this issue May 13, 2022
However, unsure how to determine capabilities for it.
See comments on the issue: #201 (comment)
This was referenced Jun 10, 2022
@xsebek
Copy link
Member

xsebek commented Jun 15, 2022

In any case, it seems weird/redundant to turn a device name into a set of capabilities just so we can then turn right around and figure out what device would provide those capabilities.

Actually with the new require "metal drill" it would be even incorrect.

I do not think the Requirement datatype would work if you had a device that provides multiple capabilities.
Maybe one level higher would - data Requirements = Requirements (Set Entity) (Set Capability)?

@byorgey
Copy link
Member Author

byorgey commented Jun 16, 2022

Actually with the new require "metal drill" it would be even incorrect.

Yes, agreed.

I do not think the Requirement datatype would work if you had a device that provides multiple capabilities.

I don't understand why this would be a problem, can you give an example?

@byorgey byorgey changed the title require command to artificially require a specific device require command to artificially require devices or supplies Jun 24, 2022
@byorgey byorgey changed the title require command to artificially require devices or supplies require commands to artificially require devices or supplies Jun 24, 2022
@byorgey
Copy link
Member Author

byorgey commented Jun 24, 2022

The other day I was programming a robot to clear an area of trees, by moving in a grid pattern, and upon encountering a tree, harvesting it and then placing a rock on top of it to prevent it from regrowing. Of course, this meant that it needed to start out with a large supply of rocks, and I had to do this:

r <- build { wait 100; ... do stuff ... }; x64 (give r "rock")

(x64 is a command I have defined which repeats an action 64 times.) This is a horrible solution for all sorts of reasons:

  • If you forget to wait, or don't wait long enough, the robot will just drive off without its needed supplies.
  • Figuring out how long you have to wait is very annoying and error-prone, especially if you need to give multiple types of supplies. In theory, wait 64 should work above instead of wait 100, but I was worried about race conditions, overhead caused by running x64 that could in theory cause each give to take longer than one tick, etc...
  • You have to add code in three separate places: the r <- at the beginning, the wait, and the instructions to give supplies at the end. If you forget any one of them it won't work.
  • As explained above, it's not compositional.

The point is, the same issue occurs with any kind of needed supplies, not just devices that need to be installed.

Concretely, I think I would now propose having two separate commands:

  • requireDevice : string -> cmd () works as described above, requiring the named device to be installed. It is idempotent, that is, calling requireDevice d two times is the same as calling it once: you still only require one device installed.
  • require : int -> string -> cmd () requires a certain number of the named item to be present in the inventory. Multiple calls to require are additive, that is, require m x; require n x is equivalent to require (m + n) x.

I think something like the above discussed Requirement type can still work, but obviously it will be a bit more complex now since it will have to distinguish among three types of requirements: requiring a capability, requiring a device, or requiring a certain number of an entity.

@TristanCacqueray
Copy link
Collaborator

Perhaps we could use a more general synchronization primitive, e.g. receive : string -> cmd () that wait until an entity (or maybe string message) is given. Then the program could be written as:

r <- build { receive "unit"; ... }; x64 (give r "rock"); give r "unit"

@byorgey
Copy link
Member Author

byorgey commented Jun 25, 2022

I like the idea of a receive command, though you can program it yourself now, something like receive = \thing. waitUntil (has thing), given an appropriate definition of waitUntil (as in #475). I'm not sure what would be gained by making it built in. In any case, that solves the second problem I mentioned (the annoyance of having to figure out how long to wait), but it still doesn't solve the other issues.

@byorgey byorgey self-assigned this Jul 1, 2022
@byorgey
Copy link
Member Author

byorgey commented Jul 2, 2022

Started on this in the require branch.

mergify bot pushed a commit that referenced this issue Jul 5, 2022
Add new special syntactic forms `require <string literal>` to require a device to be installed, and `require <int literal> <string literal>` to require a certain number of a given entity in the inventory.

- Replace `Set Capability` with a new `Requirements` type which records various types of requirement.
- Refactor requirements checking into a new function `checkRequirements` which also does device set minimization (#508).  
- `reprogram` will now install extra devices or transfer extra inventory as necessary.
- Add a bunch of tests.
- Add new `installed` command, similar to `has`, which checks whether a given device is installed.

Closes #201.
Closes #508.
@xsebek xsebek mentioned this issue Oct 8, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-Low Hanging Fruit Ideal issue for new contributors. L-Capability checking Capability checking determines which capabilities are required by a given piece of code. S-Nice to have The bug fix or feature would be nice but doesn't currently have much negative impact. Z-Feature A new feature to be added to the game. Z-User Experience This issue seeks to make the game more enjoyable to play.
Projects
No open projects
Status: Done
Development

Successfully merging a pull request may close this issue.

3 participants