alma
alma copied to clipboard
Implement a prefix:<exists> operator
Assuming a .has
method on all containers that can do indexing, a prefix:<exists>
macro would make it possible to write
if exists container["foo"] { ... }
rather than
if container.has("foo") { ... }
The ergonomic factor residing in the fact that we can use ordinary indexing to access the element whose existence we want to check. Normally such an access would through a NotFound
exception of some kind, so this usage is "macroish".
Very closely resembles #290. Part of why I'm filing this is that I want to shake out the exact required semantics of #214, and having more dependents on solidify
might help us understand more what it needs to be.
Very closely resembles the semantics of #244, which is not a macro. Still, there might be room for both.
What would be an idiomatic way to both delete
(#290) an element and check if it exists
, um, existed? Without breaking the Single Evaluation Rule?
tl;dr: No, seems not. But we might be able to force it to.
-
exists delete <expr>
deletes the<expr>
butexists
can't do its job, since it's not being fed<expr>
. (If this were at runtime, it would be fednone
, but sinceexists
is a macro, it gets feddelete <expr>
, and it doesn't know what to do with that.) Besides which, the order of the prefixes looks wrong — it looks like we're deleting, then checking existence. So even if it did work, it'd always returnfalse
. -
delete exists <expr>
checks the existence of<expr>
, but thendelete
is equally stumped byexists <expr>
. You can'tdelete
that; it wants an access path.
All of this means that we won't automatically get exists-delete behavior out of juxtaposing the two. But maybe one could be aware of the other, and do something clever, and we'd get a combined bit of generated code which Does The Right Thing?
(The right thing, to spell it out, being: take the access path, check if the thing exists (and store that in a temporary), delete the thing (which might not exist), and return the stored temporary value. It's like a delete
, but instead of returning none
, it returns the true
or false
exists
would've returned.)
If that's too tall an order, we could always have a deleteExists
prefix. But I'd prefer to do it with the orthogonal components if there is a nice way to do that.
Coming back to the above musing: there doesn't seem to be a way to do this without making delete
and exists
suspiciously aware of each other. Or, alternatively, inventing an interface/protocol by which they could communicate with each other without knowing about the details about each other's existence... hm.
But I think it's overkill to introduce such an interface/protocol just for the (fairly uncommon) pairing of delete
and exists
. I'd even be more fine with encouraging people to declare their own deleteExists
macro when the situation calls for it.
Except if there are a lot of other evaluation-tweaking macros in the style of exists
and delete
. Then I might consider it to be worth it. But I can't think of any. expect
/assert
(#417) is kind of one, but it doesn't have the access path thing going.
Oh, and just like in #290, we could use safe navigation with exists
and make "earlier" missing parts of the path an error. With delete
, the behavior was just to give up early; with the vanilla version, return none
early; here we'd want to return false
early.
Coming back to this issue, and reacting to my earlier confused musings about exists delete
and delete exists
:
-
Forbid
exists delete <expr>
. If it worked, and evaluated things in the order we'd expect from prefixes, we'd only getfalse
every time anyway. -
Make
exists <expr>
an "honorary access path", so that it somehow transparently passes up the access path in<expr>
, so thatdelete exists <expr>
works. Makedelete
aware ofexists
, so that it wires itself in that case to callexists
and return the boolean. Havedelete
promise not to re-evaluate the whole access path, in case it has side effects.