Open pyx opened 7 years ago
For maximum flexibility, a macro has to be able to interpret a keyword as as keyword instead of a kwarg pair. This way you can write macros that contain keywords as part of the syntax, even if there are two in a row or one at the end. I think you could simply have the macro call a function that accepts &kwargs
. No need to parse it yourself.
Revise, remove excessive do
(defmacro/g! time-it [expr &optional setup round]
`((fn []
(import [timeit [timeit :as ~g!timeit]])
~(when setup setup)
(defn testee [] ~expr)
(setv kwargs {})
~(when round `(assoc kwargs "number" ~round))
(apply ~g!timeit [testee] kwargs))))
In the beginning, I did not wrap everything inside a function, thus the do
form, when I switched to function (to have local scope), I forgot to remove the do
@gilch Ah, that's a nice idea, a helper function will do, indeed. Thanks. I will come back to this when I have time, I need some fresh air now.
A simple example:
=> (defmacro foo [x &rest xs] `((fn [s &kwargs kws] (, s kws)) '~x ~@xs))
=> (foo norf :bar 1 :baz 2)
('norf', {'bar': 1, 'baz': 2})
Note that the macro can quote the first symbol and still parse the remaining kwargs.
Okay, this is what I have so far
(defmacro/g! time-it [expr &rest options]
(if
(not (keyword? (first options)))
(setv setup (first options) args (rest options))
(in :setup options)
(do
(setv index (.index options (keyword "setup")))
(when (= index (dec (len options)))
(macro-error None "Keyword argument :setup needs a value."))
(setv skipped (, index (inc index))
setup (get options (inc index))
args (list-comp exp
[(, i exp) (enumerate options)]
(not-in i skipped))))
(setv setup None args options))
`((fn []
(import [timeit [timeit :as ~g!timeit]])
~(when setup setup)
(~g!timeit (fn [] ~expr) "pass" ~@args))))
Test drive:
hy 0.11.0+353.gca6fd66 using CPython(default) 3.5.2 on Linux
=> (defmacro/g! time-it [expr &rest options]
... (if
... (not (keyword? (first options)))
... (setv setup (first options) args (rest options))
... (in :setup options)
... (do
... (setv index (.index options (keyword "setup")))
... (when (= index (dec (len options)))
... (macro-error None "Keyword argument :setup needs a value."))
... (setv skipped (, index (inc index))
... setup (get options (inc index))
... args (list-comp exp
... [(, i exp) (enumerate options)]
... (not-in i skipped))))
... (setv setup None args options))
... `((fn []
... (import [timeit [timeit :as ~g!timeit]])
... ~(when setup setup)
... (~g!timeit (fn [] ~expr) "pass" ~@args))))
=> (time-it (inc 1))
0.6054470939998282
=> (time-it (inc answer))
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 1, in _hy_anon_fn_2
File "/usr/lib64/python3.5/timeit.py", line 213, in timeit
return Timer(stmt, setup, timer, globals).timeit(number)
File "/usr/lib64/python3.5/timeit.py", line 178, in timeit
timing = self.inner(it, self.timer)
File "<timeit-src>", line 6, in inner
File "<input>", line 1, in _hy_anon_fn_1
NameError: name 'answer' is not defined
=> (time-it (inc answer) (setv answer 42))
0.6117291800001112
=> answer
Traceback (most recent call last):
File "<input>", line 1, in <module>
NameError: name 'answer' is not defined
=> (time-it (print 'Hit) :number 3)
Hit
Hit
Hit
0.00010172399925068021
=> (time-it (print 'Hit) () :number 3)
Hit
Hit
Hit
9.308399967267178e-05
=> (time-it (print 'Hit) :number 3 :setup (print 'Setup...))
Setup...
Hit
Hit
Hit
7.982400347827934e-05
=> (time-it (print 'Hit) :setup (print 'Setup...) :number 3)
Setup...
Hit
Hit
Hit
8.413199975620955e-05
=> (time-it (inc 1) :setup (print 'Setup...))
Setup...
0.5945940750025329
=> (time-it (inc 1) :setup)
File "<input>", line 1, column 1
(time-it (inc 1) :setup)
^----------------------^
HyMacroExpansionError: b'Keyword argument :setup needs a value.'
=> (time-it (inc 1) :i-dont-know-what-i-am-doing 42)
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 1, in _hy_anon_fn_2
TypeError: timeit() got an unexpected keyword argument 'i_dont_know_what_i_am_doing'
As inspired by @gilch in discussion here: https://github.com/hylang/hy/pull/1179
The macro:
Another approach is using
read-str
Caveat
I've been fighting the macro system for the last hour, as we cannot use
&kwargs
withdefmacro
, so unless we use&rest
and parse the argument ourselves (I have a kind-of-working prototype, but it is so ugly that I do not dare to share), the form of the macro is a bit rigid, I skipped thetimer
argument for simplicity's sake, and right now, one have to pass in thesetup
code to specify the round of iteration, see above.