glom icon indicating copy to clipboard operation
glom copied to clipboard

Safe navigation operator?

Open larspetrus opened this issue 6 years ago • 10 comments

Have you considered adding a "safe navigation operator" to Glom?

It could be very elegant and powerful to use a target of a?.b?.c, and it's a well established feature in several languages.

https://en.wikipedia.org/wiki/Safe_navigation_operator

larspetrus avatar May 10 '18 20:05 larspetrus

Hmm, how would this differ from the current behavior of glom(target, 'a.b.c', default=None), for instance?

Also while it's apparently not the same as a null-coalescing operator (a fact I just learned thanks to you :) ), this may be relevant: http://glom.readthedocs.io/en/latest/faq.html#does-python-need-a-null-coalescing-operator

mahmoud avatar May 10 '18 22:05 mahmoud

That looks like a decent replacement for my example and many other cases, but I have often done things like a.b?.c in other languages. Currently I think you'd have to do something non-glom to express that?

It could also be nice to have a simple compact notation for the default=None case.

larspetrus avatar May 11 '18 03:05 larspetrus

i actually wrote this yesterday, using the unary invert ~ operator. it's basically just sugar around glom(target, T(...), default=None). example usage:

safe = U(obj)
value = ~safe.a.b[0].c

implementation:

class U(object):
  def __init__(self, obj, t=glom.T, default=None):
    self.obj = obj
    self.t = t
    self.default = default

  def __getitem__(self, name):
    return U(self.obj, self.t[name])

  __getattr__ = __getitem__

  def __invert__(self):
    return glom.glom(self.obj, self.t, default=self.default)

note that this also changes attribute-style . access to item access instead, like many templating languages. you can remove that by changing __getattr__ = __getitem__ to something like:

  def __getattr__(self, name):
    return U(self.obj, getattr(self.t, name))

snarfed avatar May 11 '18 14:05 snarfed

Sorry, my opinions are stronger than my Python skills.

Is this code a way to make your own safe operator that works with glom?

larspetrus avatar May 16 '18 03:05 larspetrus

yes. it's pretty much exactly what you asked for. :P

snarfed avatar May 16 '18 04:05 snarfed

a.b?.c could be implemented as glom(target, ('a.b', Coalesce('c', default=None))) -- if I understand the intended semantics properly "error if a doesn't have a b attribute, but if b doesn't have a c return None instead"

Coalesce() combined with tuple-chaining lets you fiddle "in the middle" of the operations; this is very wordy though

to go the other direction, if you has a?.b.c is the intended behavior "if a doesn't have a b, immediately return None?" or "if a doesn't have b, substitute None then fail because None doesn't have a c?" -- that is, are the semantics exactly the same as get()?

oh! that reminds me, you can also do T.get() or Call(getattr) -- not that any of these are the same as having native support in Path just other stuff in the neighborhood

kurtbrose avatar May 16 '18 22:05 kurtbrose

this definitely bears more discussion, thank you very much for raising the issue :-)

kurtbrose avatar May 16 '18 22:05 kurtbrose

yes. it's pretty much exactly what you asked for. :P

Unless I'm really confused, it's not the same as being able to write glom(data, 'a.b?.c'), which is what I was suggesting.

Now, I'm not asking for anything. I just thought this could possibly be a nice feature idea for your library. If you don't think it is, you're most likely right!

larspetrus avatar May 17 '18 05:05 larspetrus

Hey @snarfed, that's some pretty interesting shorthand! I'll have to keep invert in mind for future syntax enhancements :)

@larspetrus This is definitely an interesting case. I see the difference now, in the ability to control which segments are required, and which should be optionally traversed. Also, if Coalesce() is the equivalent of a Python or, then a safe navigation operator is sort of like saying output = a and a.b and a.b.c.

Now, I'm not sure how often I've needed this fine-grained of control (i.e., at the sub-line level), but I'll keep an extra eye out going forward. If it seems like a powerful enough feature, you can bet it'll get integrated. Thanks!

mahmoud avatar May 17 '18 06:05 mahmoud

Glad I could raise awareness :)

larspetrus avatar May 17 '18 15:05 larspetrus