-
Notifications
You must be signed in to change notification settings - Fork 372
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
Rethink fn arguments list #1411
Comments
Option 2(defn foo [a {b 1 c :c d None} args & x {y 2 z 3} kwargs])
(defn akw [{} args & {} kwargs])
(defn only [& x {y 2}]) def foo(a, b=1, c=HyKeyword("c"), d=None, *args, x, y=2, z=3, **kwargs): pass
def akw(*args, *kwargs):pass
def only(*, x, y=2):pass Here, optional args go in The And finally the kwargs follows that dict. So This gives macros more (and more consistent) structure to work with. I'm not sure how This seems to handle |
I think it's better to save the brackets for macro destructuring syntax. Can we avoid them in the special form?
|
|
|
It looks like the ordering of kwonlys is inspectable. So decorator metaprogramming could potentially use that to mean something important. Hy has a lot of edge cases like this that should be corrected, but let's not make it worse. We're back to Option 1, the only option so far without this problem. |
Option 5
|
Option 6(defn foo [a &? b 1 c c d None &* args x . y 2 z . &** kwargs])
(defn akw [&* args &** kwargs])
(defn only [&* . x . y 2 z .]) def foo(a, b=1, c=c, d=None, *args, x, y=2, z, **kwargs): pass
def akw(*args, **kwargs): pass
def only(*, x, y=2, z): pass
I like this version best so far, and will probably implement Maybe we could use something other than |
Option 6 bUse Option 6 cUse |
I personally started using kwonly arguments without default liberally, and making those harder to understand at a glance does the feature a misservice, imho (requiring the extra Also think its weird that You also really should be forward looking a bit here as there's a good chance positional only arguments will come. They're already a thing in the CPython core, just without syntax for Python to leverage this - for now. It'll likely look like this In [1]: range?
Init signature: range(self, /, *args, **kwargs) I think we really only need a few things:
Building on Option 6, embracing tuples for optional arguments, as they're already done, does simplify things. Then And technically, we don't really have to allow the end user to choose the variable name for (defn foo [a [b 1] [c c] [d None] &args x [y 2] z &kwargs])
(defn akw [&args &kwargs])
(defn only [&* x [y 2] z]) And to add, it would be easy to add positional only after the fact: (defn my-range [self &/ &args &kwargs]) |
Necro'ing this as part of a rethinking of :keywords in general. I'd like to move forward with @vodik's proposal, with some minor tweaks: (defn foo [i j &/ a [b 1] [c c] [d] &args x [y 2] z &kwargs])
(defn akw [&args :as vs &kwargs :as kws])
(defn only [&* x [y 2] z])
;; also, a new proposed calling syntax:
(foo "i" "j" "a" "b" & x "ecks" z "zed" d "dee" other "extra thing") def foo(a, b=1, c=c, d=None, *args, x, y=2, z, **kwargs): pass
def vkw(*vs, **kws): pass
def only(*, x, y=2, z): pass
foo("i", "j", "a", "b", x="ecks", z="zed", d="dee", other="extra thing") This makes function defs/calls have a syntax closer to some of the other Otherwise, like @vodik's proposal, we're just reusing python's existing In addition, I'd like to propose using symbols as symbols instead of :keywords The new calling syntax should be unambiguous since (just as with python) positional arguments must always come before keyword arguments. |
The "new" proposed calling syntax is pretty close to how Hissp does it. Except Hissp uses |
I'm not a fan. The parameter names look too much like expressions, and it will no longer be possible to intermingle positional and keyword arguments. |
I was not aware of Hissp! Good to know.
Would it be better to use bracketed pairs after the keyword marker? (foo "i" "j" "a" & [x "ecks"] [z "zed"] "b" [d "dee"] [other "extra thing"]) Ultimately I'd like to get rid of using HyKeywords for python keyword arguments for the linked reasons above, so that they're not pulling double duty as both control words and symbol names. |
No, in my opinion, that's quite noisy, and I don't know what you mean by "pulling double duty as both control words and symbol names". Constructing keyword arguments is the only real purpose of HyKeyword. |
I also quite like keywords in function calls visually, but would it be possible then to only allow keywords in function calls? Essentially codifying the fact that they only exist to construct keyword arguments? I feel like a lot of the busy work of hy fn defs would be solved by just getting rid of |
In Lisp, we try to allow the use of code as data as much as possible. So, that seems counterproductive.
|
makes sense. what about this for fn args: (defn test [a b &/ c [d 1] &* e [f 2] [&** kwargs]]) would compile to: def test(a, b, /, c, d=1, *, e, f=2, **kwargs): Instead of |
Not bad. I would say we should use |
then how would you identify kwonly args from the rest assignment? |
|
That feels like it lacks clarity to me and is inconsistent with the existing bracket syntax. |
It makes sense to me that binding |
So to be clear, we're considering this for the latest syntax? (defn foo [i j / a [b 1] [c c] [d] #* args x [y 2] z #** kwargs])
(defn akw [#* vs #** kws])
(defn only [* x [y 2] z])
(foo "i" "j" "a" "b" :x "ecks" :z "zed" :d "dee" :other "extra thing") def foo(a, b=1, c=c, d=None, *args, x, y=2, z, **kwargs): pass
def vkw(*vs, **kws): pass
def only(*, x, y=2, z): pass
foo("i", "j", "a", "b", x="ecks", z="zed", d="dee", other="extra thing") |
Yes, except I wouldn't implement |
Good point. I think I'm happy to concede at this point.
Also, I almost have a working prototype, i'll let y'all know hwo that turns out |
how should annotations for varargs and kwargs be specified? |
|
In that case I have something working (the other way would make the special form a lot messier). I'll clean it up and open a pr for y'all to mess around with tonight => (fn* [a ^int b / c [d 1] * ^str e ^int [f 2] g ^str #** kwargs])
def _hy_anon_var_4(a, b: int, /, c, d=1, *, e: str, f: int=2, g, **kwargs: str):
pass |
Hy currently has
&optional
,&key
,&kwargs
,&rest
, and&kwonly
.Python does not seem this complicated. It only has
*
and**
to remember. Clojure only has&
, but it can use that with its dict destructure to emulate kwargs. It's an elegant solution, but I don't think it's good enough for Hy, which needs to interoperate closely with Python.To be fair, Hy is not any worse than Common Lisp's
&allow-other-keys &environment &rest &aux &key &whole &body &optional
.Is there any way we can simplify this? Any simplification here would be a very deep change, and bound to be controversial. But it's easier to build advanced macros on simpler, more consistent special forms. The time for breaking changes is sooner, not later.
I think the special form should follow Python as closely as possible. I don't know that I ever use
&key
, but that kind of thing probably belongs in a macro built on top offn
, not in the special form itself.If we re-do
#*
and#**
to create their own Hy models, as discussed in hylang/hyrule#52, then we could also use these in the arguments list. This would make Hy more consistent with Python, and with its own other features that use that syntax.But that would only take care of
&rest
and&kwargs
. Using#*
, we can't distinguish&rest
from&kwonly
without commas. And how do we indicate default arguments without an infix=
? It can't just be a list without&optional
, or it looks like the destructuring argument bind we still have from Python2. It's also nice to be able to get aNone
default without typing it explicitly.So how else could this work?
Option 1
Maybe from this:
To this:
And the equivalent Python.
Here we're using :keyword pairs to stand for
=
. This makes it look like a Hy function call, which is more consistent and easier to remember. Note that we have to quote keywords we want to pass as data, so they aren't interpreted as a kwarg name. But function calls work the same way. (And given the strange way our keywords work currently #1352, it would actually expand toc='\ufdd0:c'
. We might want to change this.)It seems a lot easier to work with, both for humans, and in macros. There aren't two ways to do the same thing, like
&optional
and&key
. But we don't have our implicitNone
from&optional
anymore. Oh well, neither did Python. We could implement it with a macro later, maybe.And how should
&kwonly
work? We can do[#* _ x y]
, but that's not the same. It silently ignores extra arguments instead of raising an error. One possibility is a new Hy model named#*,
, which you'd use instead of#*
in those cases. That seems conceptually the same as&kwonly
though. Is there a more elegant way to do this?And finally, what about destructuring something with a default? (e.g.
def foo((a, b)=[1, 2]): pass
) Actually, Hy can't do this! #1410. And neither can Python3. No loss. Except, I'd like to add this feature in afn=:
macro #1410, #1328. What should it look like? I'm not sure.Maybe like
(defn=: foo [ #:(, a b) [1 2]])
? This (ab)uses the one of the (Hypothetical) slice notations I proposed from #541.The text was updated successfully, but these errors were encountered: