rm-hull / infix

A Clojure library for expressing LISP expressions as infix rather than prefix notation
https://www.destructuring-bind.org/infix/
MIT License
106 stars 11 forks source link

Short alias for infix? #26

Closed mars0i closed 7 years ago

mars0i commented 7 years ago

I think I'm about as comfortable with Lisp syntax as you could be, but reading complicated arithmetic expressions with prefix notation is still not as easy as reading infix notation. Thanks very much for infix.

I think you've probably chosen to intentionally avoid specifying a short alias for infix (or have I missed it?), e.g. like Incanter's $= infix operator (whose functionality is different from infixs). It's easy enough to define my own, e.g.:

(defmacro $ [& expr] `(infix ~@expr))

but there's some value to readability by others in specifying a standard alias, rather than every infix user defining their own variant. (Perhaps this is irrelevant if infix is not popular, though.)

My own preference is to use a single-character alias, but there are only a few suitable ones. $, ?, and | seem best. A capital I is another option. The single-character macro can be refered optionally, so it needn't clobber a user's own definition, although I think single-character function and macro definitions are uncommon.

Of course in itself infix is clearer than some arbitrary character since its name suggests its purpose, but I think this is a context where a single-character definition is sometimes useful. If I have a single long arithmetic calculation, it's no problem to add infix at the beginning of it. However, if I have a long series of related mathematical let bindings, adding infix each time isn't worth the trouble. Here's an example from some of my own code:

;; Original prefix version:
(defn next-p
  [wa1 wa2 wb1 wb2 p1 q1 p]
  (let [q  (- 1 p)
        p2 (- 1 p1)
        q2 (- 1 q1)
        abs-p' (* p (+ (* wa1 p1) (* wa2 p2)))
        abs-q' (* q (+ (* wa1 q1) (* wa2 q2)))]
    (/ abs-p'(+ abs-p' abs-q'))))

;; Version with the infix macro
(defn next-p
  [wa1 wa2 wb1 wb2 p1 q1 p]
  (let [q  (infix 1 - p)
        p2 (infix 1 - p1)
        q2 (infix 1 - q1)
        abs-p' (infix p * (wa1 * p1 + wa2 * p2))
        abs-q' (infix q * (wa1 * q1 + wa2 * q2))]
    (infix abs-p' / (abs-p' + abs-q'))))

;; Version with alias macro
(defn next-p
  [wa1 wa2 wb1 wb2 p1 q1 p]
  (let [q  ($ 1 - p)   ; The first three bindings were already readable with prefix notation,
        p2 ($ 1 - p1)  ; but switching back and forth from prefix to infix arithmetic in the
        q2 ($ 1 - q1)  ; same defn seems distracting.
        abs-p' ($ p * (wa1 * p1 + wa2 * p2))
        abs-q' ($ q * (wa1 * q1 + wa2 * q2))]
    ($ abs-p' / (abs-p' + abs-q'))))

(The short variable names aren't obfuscatory; they're based on common conventions in population genetics.)

rm-hull commented 7 years ago

Absolutely amenable to adding a short alias... Might be worth being consistent with Incanter and pick $= though, what do you think ?

Am away on holiday at moment, so if you could raise a PR that'd be great (please include a sentence or two and example in the readme, and add a small test), and I'll merge it & then build a new release when I get back.

mars0i commented 7 years ago

Absolutely amenable to adding a short alias...

Great. Thanks.

Am away on holiday at moment, so if you could raise a PR that'd be great (please include a sentence or two and example in the readme, and add a small test), and I'll merge it & then build a new release when I get back.

Of course--happy to do that.

Might be worth being consistent with Incanter and pick $= though, what do you think ?

I'm not sure what's best. You can read my comments below when you have time. No need to interrupt vacation. I don't think it's a big deal, in any event. I'll go ahead and submit a PR for $=, and it will be easy to change later if that seems best. It's up to you in the end, regardless.

About $= vs something else:

I thought about following Incanter's name, and I like that idea somewhat. Fewer notations to remember, Incanter users will guess correctly, etc. A two-character name is less likely to clash with someone else's use of a single-character name. The = sign in $= kind of suggests of its function.

On the other hand, a single-character name is a little bit more unobtrusive in repeated short expressions like some of those in my example. The other thing that might push toward using a name that's different from Incanter's is that outside of the main arithmetic operators, the two systems have different syntax and some different functionality. I haven't played around much with Incanter's infix, but based on the blog post, sin(2) here is (sin 2) in an Incanter infix expression. Incanter infix expressions also distribute operations over sequences in certain ways. From https://data-sorcery.org/2010/05/14/infix-math:

user> ($= [1 2 3] + 5)
(6 7 8)
user> ($= [1 2 3] * [1 2 3])
(1 4 9)

One might argue that it's less confusing to have two different names for the Incanter and infix functions. It's also confusing to remember two different names. I don't think one consideration clearly overrides the other. I don't think there are many people who use infix notation, but maybe as Clojure is used more widely (e.g. more scientific uses), this might change.

rm-hull commented 7 years ago

0.3.0 released with this change in it & docs regenerated: http://www.destructuring-bind.org/infix/infix.macros.html#var-.24.3D