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

Add (get ...) with default value #34

Open
dakrone opened this issue Aug 20, 2015 · 10 comments
Open

Add (get ...) with default value #34

dakrone opened this issue Aug 20, 2015 · 10 comments
Labels
feature New feature or request

Comments

@dakrone
Copy link

dakrone commented Aug 20, 2015

Coming from Clojure, I was expecting this to work:

(get {"foo": "bar"} "baz" "eggplant") ;; => "eggplant" in Clojure, KeyError in Hy

However, instead I get KeyErrors. I have come to find out that (get ...) is closer to Clojure's (get-in ...).

It would be great if there was a "get-with-default" equivalent, otherwise I have to fall back to Python's implementation of get:

(.get {"foo": "bar"} "baz" "eggplant") ;; => "eggplant"
@Foxboron
Copy link
Member

My suggestion on IRC is to move get as get-in and implement get with default value as third parameter.

@algernon
Copy link
Member

I like this suggestion. PR coming up in a bit.

@algernon
Copy link
Member

There's a slight problem here: Hy's (get) works on everything that supports the python subscript stuff, be that arrays, dicts, and whatnot. Trying to subscript a key or index that doesn't exist, raise different exceptions. So to make (get) truly generic, I'd have to catch all exceptions. That does not sound like something we'd want.

@algernon
Copy link
Member

So, unless someone knows a way how to do this in a sane way, I'd say (.get) ain't that bad.

@algernon
Copy link
Member

Based on the problem explained above, I'm closing this, because there's no way to properly implement it.

@MelomanCool
Copy link

A bit late to the party, but I too would like to see this feature in the standard library.

Hy's (get) works on everything that supports the python subscript stuff, be that arrays, dicts, and whatnot.

So you're implying that if a user expects (get) to work with "whatnot", then they will also expect get-with-default to work with it also?

Trying to subscript a key or index that doesn't exist, raise different exceptions.

If we rely on exceptions, then yes, we have a problem with "whatnot", since we don't know what could it raise.

So to make (get) truly generic, I'd have to catch all exceptions.

Well, there are other options.

Instead of relying on exceptions, we could check keys of a dict or length of a list. There's still a problem with "whatever", but at least we won't need to catch all exceptions.

Instead of making a truly generic get-with-default, maybe it will be enough to only support dict- and list-like collections? Then, if a container raises another exception, we will not catch it. Or catch it and raise another exception, stating that container is not supported. Or, if we don't rely on exceptions, but can't check the length or the keys of a container, also raise an "unsupported" exception.

Instead of putting this feature inside the (get), maybe it will better to create something separate like (get-default)? That will not break the existing API and the user will be aware that this function may behave differently.

@MelomanCool
Copy link

MelomanCool commented Apr 13, 2018

I would also like to point out that Clojure's get, which OP mentioned, doesn't rely on exceptions, but checks map's keys and length of an array or a string. And for unsupported/null collections it simply returns the default value.

@Kodiologist
Copy link
Member

Okay, I think those could be reasonable compromises.

@Kodiologist Kodiologist reopened this Apr 13, 2018
@gilch
Copy link
Member

gilch commented Apr 14, 2018

That will not break the existing API

I'm not too worried about that at the moment. We've been breaking API every release, and have more breaking changes planned.

That said, Hy's get is not the same as Clojure's get-in, which puts the keys list in brackets and also takes a default afterwards. Rather, it's the special form that corresponds to Python's [] (subscript) operator. Like + (and many other Python binary operators) it's been adjusted to be variable-arity to better work in a Lisp. Note that Python's operator module calls this getitem/setitem/delitem. So perhaps item would be a better name for it.

Clojure's get, which OP mentioned, doesn't rely on exceptions, but checks map's keys and length of an array or a string.

Exactly how would you compile that? Catching the exception might actually be easier. Both IndexError and KeyError are instances of LookupError, so we could catch them both. But I'm worried we might catch an exception we shouldn't.

@gilch
Copy link
Member

gilch commented Apr 14, 2018

Thinking about this some more, we could probably write a macro to do this, and avoid catching extra exceptions with gensyms.

(defmacro/g! get [coll key &optional default]
  `(do
     ;; eval coll & key outside try
     (setv ~g!coll ~coll
           ~g!key ~key)
     (try
       (. ~g!coll[~g!key])
       (except [LookupError]
         ~default))))

But this doesn't really seem better than a simple function. The only advantage is that it doesn't have to evaluate the default expression.

def get(coll, key, default=None):
    try:
        return coll[key]
    except LookupError:
        return default

But by then why not use .get?

@Kodiologist Kodiologist added the feature New feature or request label Jun 21, 2019
@Kodiologist Kodiologist transferred this issue from hylang/hy Mar 5, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants