-
Notifications
You must be signed in to change notification settings - Fork 3
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
Loopy Clauses Without Loopy Loop #109
Comments
Not that it affects your point, but I'll state for the record that These things have their place, but yes, I agree that For clarity, in case I missed it:
|
Yes. As I imagined,
Is this example I gave what you're referring to with I also want to clarify I wouldn't say I want to avoid them. I wouldn't go out of my way not to use them in a situation when they are useful. However, I want to use them if they are necessary. (iter (dolist (a '(1 2 3))
(sum sums a))
(dolist (b '(dog cat mouse))
(collect animals b))
(return (list sums animals))) However, with (loopy-block nil
(dolist (a '(1 2 3))
(sum sums a))
(dolist (b '(dog cat mouse))
(collect animals b))
(list sums animals))
I would not say I found (with (a 1) (b 2) (c 3))
(while clause
(expr key (pop clause))
(expr (key-fn take-fn transform-fn) (--first (funcall (car it) key) oo-bind-processers))
(while (and clause (not (keywordp it)) (funcall take-fn it))
(collect taken (pop clause)))
(prepending processed (funcall transform-fn (list (cons key taken) rest))))
processed On a sidenote, since |
I found an example where I use the Some context. I liked the 1 . One is that it is not general enough to work for other common key-value structures like plists.
Specifically, the . character the macro uses was a bad choice because it conflicts with the cons pair syntax. Though for some reason I use it in
(defun oo-keyword-intern (&rest args)
"Return ARGS as a keyword."
(declare (pure t) (side-effect-free t))
(apply #'oo-symbol-intern ":" args))
(defun oo-anaphoric-symbol-p (obj regexp)
"Return non-nil if OBJ is a special macro symbol matching REGEXP."
(and (symbolp obj) (s-matches-p regexp (symbol-name obj))))
;; I want to use loopy for this in the future. Would probably be more efficient.
(defun oo-anaphoric-symbols (form regexp &optional contains-p)
"Return the special macro symbols in FORM that match REGEXP."
(-uniq (--select (or (oo-anaphoric-symbol-p it regexp)
(when contains-p (s-matches-p regexp (symbol-name it))))
(-flatten form))))
(defun oo-dot-symbols (body)
"Return a list of dot symbols in BODY."
(oo-anaphoric-symbols body (rx "." (group (1+ (not white))))))
(defun oo-dot-symbol-name (symbol)
"Return name of dot symbol."
(oo-anaphoric-symbol-name symbol (rx "." (group (1+ (not white))))))
(defun oo-anaphoric-symbol-name (symbol regexp)
"Return first group for the REGEXP matching SYMBOL."
(awhen (nth 1 (s-match regexp (symbol-name symbol)))
(intern it)))
(defmacro with-map! (map &rest body)
"Let-bind dotted symbols to their values in BODY.
This is similar to `let-alist' but map can be any key value structure."
(declare (indent 1))
(iter (dolist (dot-sym (oo-dot-symbols body))
(expr name (macroexp-quote (oo-dot-symbol-name dot-sym)))
(collect let-binds (list name dot-sym)))
(dolist (asterix-sym (oo-asterix-symbols body))
(expr name (oo-keyword-intern (oo-asterix-symbol-name asterix-sym)))
(collect let-binds (list name asterix-sym)))
(return `(map-let ,let-binds ,map ,@body)))) Example usages. ;; an alist
(with-map! '((a . 1) (b . 2) (c . 3))
(+ .a .b .c))
;; => 6
;; symbols as keys
(with-map! '(a 1 b 2 c 3)
(+ .a .b .c))
;; => 6
;; keywords as keys
(with-map! '(:a 1 :b 2 :c 3)
(+ *a *b *c))
;; => 6
;; Keys not found are bound to nil.
(with-map! '(a 1 c 3)
(list .a .b .c))
;; => (1 nil 3) I think what I can divine from my usage is that sometimes I tend to use For curiosity's sake I'll include another one too. I have tons of crazy macros in my config. This is similar to the above except when a key is not present, the body is not evaluated at all. Same thing, I kind of wanted to have the environment. I'm a bit lazy to post the helper functions here. But let me know if you want to see them too. (defmacro! when-map! (!map &rest body)
"Same as `with-map!' but if any key is not in MAP ignore body and return nil."
(declare (indent 1))
(iter (with (dots (->> (oo-dot-symbols body)
(-map #'oo-dot-symbol-name)))
(astx (->> (oo-asterix-symbols body)
(-map #'oo-asterix-symbol-name)
(-map #'oo-keyword-intern))))
(dolist (sym (append dots astx))
(collect preds `(member ',sym ,!map-keys)))
(return `(wrap! ((with-map! ,!map)
(let ((,!map-keys (map-keys ,!map))))
(when (and ,@preds)))
,@body)))) (when-map! '(a 1 c 3)
(list .a .b .c))
; => nil |
Table of Contents
iter-block
iter block
implementationiter-block
Loopy Clauses Without Loopy Loop
This issue concerns the idea of creating a macro
iter-block
that is similar toloopy-iter
except it does not put its body in a while loop by default.finding myself yearning for normal loops often
Not that
loopy
is bad (it is an excellent package), but sometimes for verysimple loops the increased syntax is a bit overkill. That in an of itself is not
a problem, you might think. I can just use
dolist
or some otherseq-doseq
. However, often I'm faced with a predicament–I want to use theminimal syntax of a loop such as
dolist
orwhile
, but I also want the powerof the loopy commands–usually loopy accumulation commands such as
collect
orappending
.Ultimately, I end up doing something like in the example below. I use
iter
only for the access it provides to loopy commands and exit it on its first
iteration, using my own loops inside of it.
loopy's convenience comes at the cost of increased syntax
Sometimes it's a pain to have
finally-return
,initially
orafter-do
. Ofcourse we need them in
loopy
andloopy
iter, because then there's nodistinguishing between something that's run before/after the loop or something
in it. But it is wordier than just plain lisp.
Nested
loopy
andloopy-iter
are also typically very syntax heavy. Having toto use
loopy
orloopy-iter
subloops when you can have all variables on onelevel can also be inconvenient.
In situations where you don't need control flow between nested loops (via
return-from
or using something like(at loop-name (leave))
) and where youalso don't need a specific iteration command that only
loopy
provides, I findthe combination of normal lisp expressions and loopy clauses very powerful. You
get the benefit of loopy commands without paying for it with increased syntax.
proposed solution:
iter-block
iter-block
orloopy-block
would be the same asloopy-iter
except itwouldn't loop by default (or even at all). It would just allow you to use all
the loopy commands that it would make sense to use in it (since it doesn't loop
continue
andskip
may not make sense). You'd be able to initialize variableswith
with
declaratively instead of having to wrap all your code. And you'd beable to use accumulation commands throughout your code.
very rough sketch of
iter block
implementationIt occured to me that perhaps the best way to nest loops is to use existing
loops combined with the power of loopy's syntax.
This is a very rough sketch of what I mean. Some problems are that using
with
and
without
won't work in my example because they need to be in the top level.example
The
loopy
"environment" is still useful even without using an iterationcommand. The most powerful thing about it in my opinion is the ability to create
variables on the fly implicity via
with
and the acumulation commands instead ofhaving to wrap a large
let
around the body.applications of
iter-block
These are some cool ideas I had using iter. The ideas are not so novel as I see
you've have similar ones by creating
loopy-let*
andloopy-lambda
. However,they have my own spin added to them.
iter-defun
We can use this to extend other macros like
defun
. In the example below I adddestructuring to
defun
by usingiter
's destructuring and body ofdefun!
would be able to use the loopy clauses in its body without having to declare a
loopy
loop.iter-defun example
In this example I have a few nested loops. The advantage is apparent: we save
some text by just using
while
for both loops as opposed to(loopy (while...))
and we can still use the rich set of loopy commands. Another plusis since the
iter-block
is not looping by default we could have it returnsomething normally–just by putting it at the end of the form (see
processed
in my example) as opposed to using
(finally-return)
.Of course if we need specific loops like looping through the cons of a list with
cons
or even if we need more complex loops, we'd useloopy
oriter
. Butfor the looping of a list, when where we're not declaring variables with
with
,it's nice to use
while
ordolist
because its syntax is more concise.The drawback we lose the ability to abort from specific loops or skip specific
loops.
skip
won't work properly. Using it would be equivalent to(return nil)
. So we lose the capability to usereturn-from
andskip
.iter-defmacro
This would be the same as
iter-defun
except fordefmacro
.conclusion
I think such a feature would be useful by providing an interesting alternative
to
loopy
anditer
. All in all, a useful tool for particular cases.The text was updated successfully, but these errors were encountered: