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

How do we feel about removing tuple unpacking in lambda lists? #1589

Closed
Kodiologist opened this issue Apr 19, 2018 · 10 comments
Closed

How do we feel about removing tuple unpacking in lambda lists? #1589

Kodiologist opened this issue Apr 19, 2018 · 10 comments

Comments

@Kodiologist
Copy link
Member

Kodiologist commented Apr 19, 2018

I'm working on #1573 and I've noticed that without this feature, lambda lists would be a lot simpler. The fundamental unit, particularly for mandatory arguments, would be just a symbol. As is, the fundamental unit can be a symbol or it can be an arbitrarily nested list of symbols. Currently, this is already broken because of the conflict with the syntax for default values (#1410). It's also worth noting that this feature was present in Python 2 but removed for Python 3; to bridge the gap, Hy implements it with temporary variables instead of Python 2's native support.

Unpacking, including much more general unpacking capabilities, could still be provided by macros (e.g., #1328).

@tuturto
Copy link
Contributor

tuturto commented Apr 19, 2018

Just to make sure, this means removing support for following or something completely else?

=> (defn foo [(, bar baz)]
...  (+ bar baz))
=> (setv test (, 1 2))
=> (foo test)
3

It's handy, but I don't see that so often. I don't have (strong) objections on removing it.

@Kodiologist
Copy link
Member Author

Yeah, that's what I mean. I didn't even realize you could write it as (defn foo [(, bar baz)] …) as well as (defn foo [[bar baz]] …).

@tuturto
Copy link
Contributor

tuturto commented Apr 19, 2018

I guess one could in theory combine that with optional parameters:

(defn foo [&optional [(, bar baz) (, 1 2)]] ...)

But then we would have to have a special case for

(defn foo [&optional (, bar baz)] ...)

is both bar and baz None? Or is the tuple None and unpacking will thrown an error? There might be other special cases too that I fail to think right now. I think it's better to remove it and do unpacking inside of the function.

@gilch
Copy link
Member

gilch commented Apr 20, 2018

#1328, (or its successor) could pretty much replace this. Let's get it out of the compiler.

I stalled when implementing that mainly because of #1414. But now that we have a let macro I might do it differently anyway. I intentionally omitted destructuring in let because it would add complexity to an already complicated macro, but I thought it would be relatively straightforward to implement a let=: macro in terms of let and something like #1328.

@ekaschalk
Copy link
Contributor

I dislike that this change applies to fn as well. One very common usecase is mapping a lambda over an enumerate. Less commonly, accumulation of multiple arguments.

;; Mapping over an enumerate
(map (fn [[idx arg]]
       (if arg idx))
     (enumerate foo))

;; Reductions
(reduce (fn [acc [destruct n-arity args]])
        [[foo bar baz] [a b c] ...])

@Kodiologist
Copy link
Member Author

One very common usecase is mapping a lambda over an enumerate.

That should be doable more easily with a comprehension (#1626).

@ekaschalk
Copy link
Contributor

OK so it would be (lfor [idx arg] (enumerate foo) ...) if I'm reading right.

I'm still hesitant as I don't see how to fit the new-style comprehensions into the threading macros.

@Kodiologist
Copy link
Member Author

Right.

@gilch
Copy link
Member

gilch commented Jun 7, 2018

I dislike that this change applies to fn as well.

I intended to replace the lambda-list unpacking with more a more general destructuring macro from #1328, which would also give you the ability to unpack mappings. There are still some open questions though, particularly #1411.

But the new pattern matching stuff from the compiler might also lead to an even more general alternative. Think Haskell-style unpacking instead of Clojure, maybe. Though Clojure has an experimental pattern matching library that might be worth emulating. I'll have to look into it more.

I don't see how to fit the new-style comprehensions into the threading macros.

Can you elaborate on that with some concrete examples? The new comprehensions should work in the threading macros about as well as Clojure's for does. Were you fitting the old ones in somehow?

@gilch
Copy link
Member

gilch commented Jun 7, 2018

I realize this is not as nice as the pending macro would be, but in the meantime, you can still unpack inside the fn body with a setv.

;; Mapping over an enumerate
(map (fn [idx,args]
       (setv [idx arg] idx,args)
       (if arg idx))
     (enumerate foo))

;; Reductions
(reduce (fn [acc destruct,n-arity,args]
          (setv [destruct n-arity args] destruct,n-arity,args))
        [[foo bar baz] [a b c] ...])

Though in the case of map at least, a gfor seems better.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants