-
Notifications
You must be signed in to change notification settings - Fork 53
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
Overhaul the entity system #455
Comments
I like the detailed proposal 👍 Would it be possible to have automatic conversion like Haskell I like the expandable item idea. Grouping items sounds useful on its own, so I may write an Issue for it. 😁 |
Short answer: no! Long answer: great question! There are two ways I can think to achieve that, but both would be a big change to the type system:
Either of these by themselves wouldn't be too bad --- I have implemented type systems with both features before. However, the thing is that in #231 we are already contemplating a pretty complex effect typing system to handle capabilities properly. If we're going to make the type system more complex, I think capabilities are the more important and relevant thing to handle. And if we tried to combine the system being contemplated in #231 with either subtyping or qualified polymorphism, I think my brain would simply explode. 🤯 |
Thinking about this a bit, do we need the random generation of entity UUIDs when we have hashes? Or would this overhaul remove the current entity hashing system? |
Re showing the numbers, we could show the name and as little of the unique identifier (or hash) as necessary to disambiguate them for current robot. Similarly to how Git shows longer SHA as the repo history increases. |
No, I was talking about unique id's just as a sort of hypothetical idea, which I then explained that we don't want. We definitely want to just use hashes.
That's a good idea. |
Now that records are part of the game, could we use them for entities? That way we might avoid adding many custom commands just for entities. |
Can you elaborate? I'm not sure I understand what you mean. |
@byorgey something like |
Oh, I see, that's a nice idea. Though to make it ergonomic would require #153 . There are probably other hidden gotchas, or extra features we would want for records to make it work well, but it's definitely worth exploring. |
Random idea I had, in reference to this question:
Above I answered "no"; however, in thinking about it more I think a compromise might be possible: we could allow text literals to check at type |
@byorgey so you are suggesting something like OverloadedStrings literals? |
No, What I am proposing is much simpler: we always assume text literals have type There would be some programs that |
The problem
There are several open issues struggling with how to deal with entities when we need to be able to distinguish among entities that would otherwise be considered identical.
box
es with things put inside them: once we can start putting things in boxes, it matters a lot which box we are referring to. For example, we want to be able to say things like "take one of my 50box
entities, put a bunch of things in it, and now give that specific box to another robot".typewriter
device for reifying values as entities #116 wonders about how to distinguish among differentpaper
entities that have had values printed on them.I'm sure other, similar situations will arise as we continue designing more interesting entities and commands.
In general, we have considered entities to be uniquely identified by their name. However, this causes a lot of problems.
box
again. Getting a reference to a specific entity #114 suggests taking abox
entity and changing its name to something unique (e.g.box1425
) so that we can now refer to it uniquely. The problem is that we now no longer know that it is abox
! As in, we might want to say something like "give every box in my inventory tobase
", but we can't: thebox1425
is no longer abox
, it is now abox1425
.string
values is error prone and not type-safe: e.g.give base "treee"
only fails at runtime.Proposed solution concept
The germ of this idea came from @noahyor . #393 noticed that principles of object-oriented programming were helpful in thinking about the way robots interact; the same thing is true here. The key components of the solution are:
entity
representing descriptions of entities; entities can only be distinguished up to structural equality.string
such asbox
) and its identity (represented internally by its hash, and in the external language as a value of typeentity
).For efficiency, we can still have multiple identical entities represented simply by a count (for example, if we have harvested 1257
tree
s, we do not need to give each one a separate, unique identity! --- we just have 1257 copies of the generictree
entity). But we can also have multiple entities with distinct identities but the sameclass
. This makes thebox
andpaper
examples above work cleanly. For example, we can refer to a specificbox
by its unique identity, but it is still abox
, so we can do things like "give allbox
es" even if some of them have distinct identities.Equivalence of entities and semantics of
entity
In OOP terms, if we imagine each individual entity (e.g. every single individual
tree
) to be an object, there are three equivalence relations on entities we might potentially care about.tree
s in your inventory, every single one would be not reference-equal to the others.tree
s in your inventory are all structurally equal, but an emptybox
and abox
containing a few other items would not be structurally equal.box
es in the previous example would be class-equal, but abox
would never be class-equal to atree
.In light of these categories, I propose:
entity
will represent entity descriptions, that is, equivalence classes of entities up to structural equality.entity
can therefore be represented internally by an entity hash value. (Yes, technically, this is not sound since there could be a hash collision for entities that are not structurally equal, but such a collision is extremely improbable.)entity
does not represent a "reference" to a specific entity (unlike, say, values of typerobot
).Proposed solution details
entity
type, similar to what we did with robots in Use a new opaque type for robots instead of strings #303 .entity
value can be stored as anInt
, the entity hash value we already compute.entity
values must be immutable, since changing an attribute of an entity changes its hash value and thus its identity.give : robot -> entity -> cmd ()
,place : entity -> cmd ()
, andgrab : cmd entity
.make
andcreate
, on the other hand, should probably not change type; more on this below.entityMap
(andentities.yaml
) should now be thought of as storing a prototypeEntity
object corresponding to each entity class name.entity : string -> entity
(alternative name:proto
?) which does a lookup in the entity map, i.e. returns the prototype entity associated with a particular class name.give base (entity "tree")
.def give' = \r. \c. give r (entity c) end
.thing <- grab; give base thing
just like before.b : entity
representing, say, a special, specific box with some things put in it, we can also saygive base b
.make : string -> cmd ()
rather thanmake : entity -> cmd ()
, since it only ever makes sense formake
to produce a prototype entity of a given class.class : entity -> string
.e : entity
and you want tomake
another one of it, you can of course saymake (class e)
.base
.insert : entity -> entity -> cmd entity
, like sotreeBox <- insert (entity "tree") (entity "box"); give base treeBox
.tree
andbox
entities from your inventory, add a new entity to your inventory representing the box with a tree in it, and return the hash value of that new entity. (The fact that it mutates your inventory is why it has to be in thecmd
monad.)insert (entity "tree") (entity "box")
twice, you will now have two identical (i.e. structurally equal) entities in your inventory.typewriter
device for reifying values as entities #116 .getUnique : string -> cmd entity
which would take one of the prototype entities of the given class and make it into a new, unique entity. However, getUnique is not necessary; in retrospect I was confused about the semantics ofentity
and the difference between structural and reference equality.getUnique
likemakeGeneric
to turn an entity back into a prototype entity; this is also unnecessary. The example given previously was turning a unique box back into a generic box after removing some items from it. But after removing items from the box, it will now be structurally equal to a prototype box, hence nothing additional needs to be done.make
will also only use prototype entities for ingredients.make "drill"
will never use that box to build the drill.make
will pick its ingredients up to structural equality with prototype entities, not up to class equality.entity
values represent entity descriptions, not entity references, makes it possible to talk about entities you don't have in your inventory.painted : color -> entity -> entity
. This is a pure function that operates on entity descriptions. For example,painted blue (entity "tree")
describes an entity which is the same as a generic prototypetree
except that it is blue. It does not matter whether you have any trees or any blue trees in your inventory.until (ishere (painted blue (entity "tree"))) { move }
, to move until you encounter a blue tree.transform : entity -> (entity -> entity) -> cmd entity
(or maybeapply
, orapplyTo
?) for taking an entity transformation function and actually applying it to an entity in your inventory. In other words,transform e f
will (1) look for an entity structurally equal toe
in your inventory (and throw an exception if none is found), (2) remove one copy of that entity; (3) insert one copy off e
into your inventory; (4) return the value off e
.inserted : entity -> entity -> entity
(as we should, in order to describe entities containing other entities), we could use that together with the proposedtransform
function to materialize entities out of thin air:transform (entity "box") (inserted (entity "tree"))
. So we will have to think more carefully about this. Maybe having a generictransform
function is not really safe after all, and we just need two versions of each transformation, one pure and one that mutates the inventory (painted
vspaint
,inserted
vsinsert
, etc.)?paint
,insert
, etc.) in thecmd
monad, but also provide a functionhypothetical : cmd entity -> entity
which performs a command in a hypothetical, infinite/creative inventory and returns a description of the resulting entity. So we could saylet blueTree = hypothetical (paint blue (entity "tree")) in ...
give base (entity "box")
will only ever give prototype boxes. So how can we do something like "give the base all the entities with classbox
"? A few thoughts:getAny : string -> cmd entity
which returns a reference to any entity of the given class in your inventory, as opposed toentity
which specifically only returns a prototype entity.has : entity -> cmd bool
vshasAny : string -> cmd bool
andcount : entity -> cmd bool
vscountAny : string -> cmd bool
.getAny
etc. are definitely useful. However,getAny
doesn't really solve the problem. It only works in the above example since we immediately get rid of each item returned bygetAny
. But in general, what if you want to do something with each box but not get rid of them? What if you want to give only some of them to the base (e.g. non-empty ones)?withAll : string -> (entity -> int -> b -> b) -> b -> b
which will fold over all entities of the given class.withAll
to implementhasAny
andcountAny
.Remaining questions
give
s you an entity, especially a non-prototype one, how do you find out about it? How do you get anentity
value corresponding to the thing you just got?receive : cmd entity
which you have to execute in order to "give permission" to another robot togive
you something, which would also give you a correspondingentity
value.cmd (() + entity)
?) it would still be annoying. e.g. imagine if thebase
had to executereceive
every time it sent a robot out to fetch something.receive
gives you the next thing from the queue. This at least makesgive
non-blocking, and means you could simply never executereceive
if you didn't care. But there is still the issue of whetherreceive
itself should be blocking or not.entity
value corresponding to them (or if you just forgot to bind the result to a variable)?The text was updated successfully, but these errors were encountered: