infix operators

379 views
Skip to first unread message

Dmitri

unread,
Nov 28, 2008, 6:54:21 PM11/28/08
to Clojure
First of I'd like to say that I find Clojure to be an excellent
language, however I find the lack of infix operators makes reading
equations somewhat unnatural, eg:

(+ (- (* x x) (* y y)) a)

I ended up writing a simple function to handle infix notation

(defn infix [arg1 func arg2 & args]
(let [result (func arg1 arg2)]
(if (= args nil) result (recur result (first args) (second
args) (rrest args)))))

using that I find makes the code more readable:

(infix (infix x * x) - (infix y * y) + a)

I was wondering if there is a more elegant way to do this, and if it
could be added as a standard or contrib function.

.Bill Smith

unread,
Nov 28, 2008, 9:34:17 PM11/28/08
to Clojure
I can understand how that might be attractive to someone accustomed to
a language that uses infix operators (as opposed to a Lisp), but I
wonder how many people with a few weeks of Lisp under their belt would
find the infix function worth the trouble.

This reminds me of when we introduced the C language at Schlumberger.
There was a fellow who was accustomed using Pascal. Rather than
adjust to C syntax, he wrote a set of macros that made C look like
Pascal.

Bill

Randall R Schulz

unread,
Nov 28, 2008, 9:56:25 PM11/28/08
to clo...@googlegroups.com
On Friday 28 November 2008 15:54, Dmitri wrote:
> First of I'd like to say that I find Clojure to be an excellent
> language, however I find the lack of infix operators makes reading
> equations somewhat unnatural, eg:
>
> (+ (- (* x x) (* y y)) a)

Try this:

(+
(-
(* x x)
(* y y))
a)

Crushing expressions into small spaces is not a virtue in either infix
or prefix.


> I ended up writing a simple function to handle infix notation
>
> (defn infix [arg1 func arg2 & args]
> (let [result (func arg1 arg2)]
> (if (= args nil) result (recur result (first args) (second
> args) (rrest args)))))
>
> using that I find makes the code more readable:
>
> (infix (infix x * x) - (infix y * y) + a)

If you're not going to parse fully arbitrary arithmetic expressions
according to the usual (which may as well be arbitrary) operator
precedence, then it's hardly worth it. And it seems to me that the
syntactic significance of parentheses in infix notation is completely
at odds with their role in prefix / S-Expression notation. Thus it
seems that the two are not really compatible.

Ultimately, the superiority of infix notation obtains only in a very
narrow and restricted subset of expressions and this does not, in the
end, justify its incorporation into a fine, elegant language based on
S-Expressions.


> I was wondering if there is a more elegant way to do this, and if it
> could be added as a standard or contrib function.

I don't think so. I would not mind being shown wrong, but I really don't
think so.

As an aside, I'll mention that I read a lot of papers in the domain of
mathematical logic. There the operators are quantifiers (always
prefix), boolean connectives (unary, nonassociative binary or
associative / commutative binary operators) and named predicates and
functions. The convention in such publications is to use prefix for the
unary operators, infix for the nonassociative and A/C binary operators
with a conventional operator precedence and prefix using fully
parenthsized argument lists for named predicates and functions (with
the usual gratuitous parentheses separating arguments). And I'll tell
you, only the simplest such formulas are readily comprehensible. Using
a KIF- or CLIF-like notation (which would appear to be Lisp-like to
denizens of this list) would immensely improve the comprehensibility of
these publications!


Randall Schulz

Dmitri

unread,
Nov 28, 2008, 11:11:32 PM11/28/08
to Clojure
Thanks for the comments, the prefix notation may indeed be something
that one gets used to. I find it just fine for most cases, just not
for mathematical expressions. The example function was not meant as a
complete solution, but rather as an example of how trivial it is to
switch between the two notations. I do see how the formatting makes it
more readable, thanks for pointing that out. It does sound that it's
largely a matter of what you're used to.

Jeff Bester

unread,
Nov 30, 2008, 1:11:55 AM11/30/08
to Clojure
If you are translating formulas it might be worth investing the time
to
create a macro to convert from infix to prefix with precedence rules,
as
well as, creating new operators. I think Peter Norvig covers
something
akin to this in PAIP. This is where any lisp truly shines; that is
using
macros to express Domain Specific Languages. That is at compile/load
time
morph the code to allow new syntax in the language.

See the following for an example implementation

user> (formula (3 + 4 * 2) / 3)
11/3

===== Example code ====

;; used for order of evaluation table and for valid infix operators
(def +precedence+
{'rem 5,
'* 4,
'/ 3,
'+ 2,
'- 1})

;; highest level of precedence
(def +highest-precedence+ (apply max (map val +precedence+)))

(defn- operator?
"Check if is valid operator"
([sym]
(not (nil? (get +precedence+ sym)))))

(defn- find-lowest-precedence
"find the operator with lowest precedence; search from left to
right"
([seq]
;; loop through terms in the sequence
(loop [idx 0
seq seq
lowest-idx nil
lowest-prec +highest-precedence+]
;; nothing left to process
(if (empty? seq)
;; return lowest found
lowest-idx
;; otherwise check if current term is lower
(let [prec (get +precedence+ (first seq))]
;; is of lower or equal precedence
(if (and prec (<= prec lowest-prec))
(recur (inc idx) (rest seq)
idx prec)
;; is of high precedence therefore skip for now
(recur (inc idx) (rest seq)
lowest-idx lowest-prec)))))))

(defn- infix-to-prefix
"Convert from infix notation to prefix notation"
([seq]
(cond
;; handle term only
(not (seq? seq)) seq
;; handle sequence containing one term (i.e. handle parens)
(= (count seq) 1) (infix-to-prefix (first seq))
;; handle all other cases
true (let [lowest (find-lowest-precedence seq)]
(if (nil? lowest) ;; nothing to split
seq
;; (a b c) bind a to hd, c to tl, and b to op
(let [[hd tl] (split-at lowest seq)
op (first tl)
tl (rest tl)]
;; recurse
(list op (infix-to-prefix hd) (infix-to-prefix tl))))))))

(defmacro formula
"Formula macro translates from infix to prefix"
([& equation]
(infix-to-prefix equation))


Johan Berntsson

unread,
Nov 30, 2008, 2:35:02 AM11/30/08
to Clojure
As most people will tell you, the prefix notation is more natural in
Lisp-like languages. However, I sometimes wonder if adding a Haskell
infix operator (grave accent changes the argument order) could be a
good idea in some situations; for example (2 `+ 3) -> (+ 2 3).

Mark Volkmann

unread,
Nov 30, 2008, 6:49:06 AM11/30/08
to clo...@googlegroups.com
On Sun, Nov 30, 2008 at 12:11 AM, Jeff Bester <jbe...@gmail.com> wrote:

snip

> ;; used for order of evaluation table and for valid infix operators
> (def +precedence+
> {'rem 5,
> '* 4,
> '/ 3,
> '+ 2,
> '- 1})

What's the significance of this map being named with a leading and
trailing plus? I haven't encountered that before.

--
R. Mark Volkmann
Object Computing, Inc.

Tom Faulhaber

unread,
Nov 30, 2008, 1:33:01 AM11/30/08
to Clojure
Bill,

Actually, the original Unix shell was written in this fashion by Steve
Bourne at Bell Labs.

In those days, people thought the ability to do this sort of thing was
one of the advantages of the C preprocessor. It didn't take too long
for them to change their minds. :-)

At Harvard back in the 70s, we had a language called EL1 (Extensible
Language 1 - primarily developed by Ben Wegbreit and Tom Cheatham)
that had a Algol like syntax on top and was really all s-expressions
underneath. The thing that was cool about this was a super-macro
facility called "rewrite rules" that let you really change the syntax
of the language.

You could do some nice things with it, but the users ended up breaking
into two camps: the ones who really understood it just wished that the
Algol stuff would get out of the way and let them see the Lisp (which
you could in the debugger) and the others never were really able to
leverage the power of the macros because they didn't really see what
was going on.

Tom

Randall R Schulz

unread,
Nov 30, 2008, 9:06:24 AM11/30/08
to clo...@googlegroups.com
On Saturday 29 November 2008 22:33, Tom Faulhaber wrote:
> Bill,
>
> Actually, the original Unix shell was written in this fashion by
> Steve Bourne at Bell Labs.

Wow. That takes me back. I remember seeing that code once (I was at an
educational institution that had a full source license for Bell Labs
Unix at the time). If I recall, it also did some funky stuff for memory
management. It would just increment a global pointer when it needed
more memory and if it got a segmentation violation signal when
attempting to access that memory, it would sbrk() some more and return
from the signal, allowing the program to continue with the formerly
erring instruction. It was horribly machine- and compiler-dependent,
obviously.

It was all quite grotesque!


> ...
>
> Tom


Randall Schulz

Randall R Schulz

unread,
Nov 30, 2008, 9:33:00 AM11/30/08
to clo...@googlegroups.com
On Saturday 29 November 2008 22:11, Jeff Bester wrote:
> ...

>
> If you are translating formulas it might be worth investing the time
> to create a macro to convert from infix to prefix with precedence
> rules, as well as, creating new operators. I think Peter Norvig
> covers something akin to this in PAIP. This is where any lisp truly
> shines; that is using macros to express Domain Specific Languages.
> That is at compile/load time morph the code to allow new syntax in the
> language.

I don't think this strategy will work, 'cause you're operating behind
the Clojure reader. If you really want to pursue non-S-expression
parsing, then I believe you must also write a lexical analyzer and
operate from the character level.

Prolog, Scala and even Prover9 (a first-order logic theorem prover) have
user-definable operators with user-specified precedence, associativity
and "fixity" (pre-, in- or post-). It's certainly intriguing from the
perspective of creativity and expressivity, but I'm not sure how good
an idea it is to allow such complete and radical redefinition of a
program's language and syntax.

I do, however, still support (and actively desire) Common-Lisp-style
programmable reader macros. I don't accept the language fragmentation
argument, frankly. I would not expect it to be used to change Clojure
itself just to suit personal tastes, but rather to enable the
processing of prefix / S-Expression languages that are not compatible
with Clojure per se (two existing examples, one of key importance to
me, come to mind: CycL and CLIF; both are not readable with Clojure but
could be if the reader had Common-Lisp-like programmable reader
macros).


> ...


Randall Schulz

Dmitri

unread,
Nov 30, 2008, 10:03:42 AM11/30/08
to Clojure
Thanks for the example, the macro is exactly the solution was looking
for.

Daniel Renfer

unread,
Nov 30, 2008, 10:06:31 AM11/30/08
to clo...@googlegroups.com
Since it's pretty much the topic, has anyone ever seen this:

http://www.dwheeler.com/readable/

Randall R Schulz

unread,
Nov 30, 2008, 10:20:21 AM11/30/08
to clo...@googlegroups.com
On Sunday 30 November 2008 07:06, Daniel Renfer wrote:
> Since it's pretty much the topic, has anyone ever seen this:
>
> http://www.dwheeler.com/readable/

One thing I'll say is that I can't see myself _ever_ getting behind a
notation where white-space is significant. The so-called "semicolon
inference" in Groovy bugs me endlessly (in real-life programming, not
just 'cause I'm aware it's there). And there have been complaints about
the Scala counterpart on its mailing lists. Wasn't FORTRAN lesson
enough about the mistake of giving syntactic significance to blanks
beyond being simple token separators?

Frankly, I don't see the problem with S-Expressions as we've known them
for so long. I think they're beautiful. All non-trivial programs are
complex and require tool support for their creation and even more for
later comprehension. Once you accept that, then you realize that a
uniform notation like the S-Expression is just something that requires
support from the authoring and analysis tools.


Randall Schulz

Dmitri

unread,
Nov 30, 2008, 11:37:56 AM11/30/08
to Clojure
I agree that the consistency that the s-expressions provide is
valuable, and hence it would be counter productive to allow different
kinds of syntax. However, it makes sense to have an explicit way to do
infix notation. As Johan points out above, Haskell has a very elegant
way of infixing functions eg:

div a b
can be written as
a `div` b

And it seems that using a macro which accepts arguments in infix
notation would allow the code to be more expressive and readable. The
formula example Jeff provides is quite readable without requiring any
additional language features.

A general version which does not check operator precedence would
provide a way to use infix notation to make the code more readable.

Randall R Schulz

unread,
Nov 30, 2008, 12:01:04 PM11/30/08
to clo...@googlegroups.com
On Sunday 30 November 2008 08:37, Dmitri wrote:
> I agree that the consistency that the s-expressions provide is
> valuable, and hence it would be counter productive to allow different
> kinds of syntax. However, it makes sense to have an explicit way to
> do infix notation.

That seems self-contradictory.


> As Johan points out above, Haskell has a very
> elegant way of infixing functions eg:
>
> div a b
> can be written as
> a `div` b
>
> And it seems that using a macro which accepts arguments in infix
> notation would allow the code to be more expressive and readable.

Maybe, but only if it's possible, and I don't think it is with
sufficient generality to be worthwhile.


> The formula example Jeff provides is quite readable without requiring
> any additional language features.
>
> A general version which does not check operator precedence would
> provide a way to use infix notation to make the code more readable.

Infix without at least an operator precedence scheme is not going to
make anyone more comfortable, since you'll have to fully parenthesize
everything anyway or else limit it to associative-commutative
functions.


I just think this is a very wrong-headed way to approach a Lisp. The
language is what it is and you should meet it head-on and use it on its
own terms.

If you like syntactic sugar, including programmer-defined infix
operators, check out Scala. It's got syntactic sugar in spades.


Randall Schulz

Stuart Sierra

unread,
Nov 30, 2008, 2:09:12 PM11/30/08
to Clojure
On Nov 28, 6:54 pm, Dmitri <dmitri.sotni...@gmail.com> wrote:
> First of I'd like to say that I find Clojure to be an excellent
> language, however I find the lack of infix operators makes reading
> equations somewhat unnatural, eg:

Hi, Dmitri,

Glad you like Clojure! There are Common Lisp packages like <http://
www.cliki.net/infix> that do infix-conversion. I don't find infix
notation useful except when transcribing formulas, and fractions or
roots usually force me to use parentheses anyway. Sun has an
interesting experimental language called Fortress, <http://
projectfortress.sun.com/>, that uses real mathematical notation.

-Stuart Sierra

André Thieme

unread,
Nov 30, 2008, 4:07:01 PM11/30/08
to Clojure
On 30 Nov., 12:49, "Mark Volkmann" <r.mark.volkm...@gmail.com> wrote:
> On Sun, Nov 30, 2008 at 12:11 AM, Jeff Bester <jbes...@gmail.com> wrote:
>
> > ;; used for order of evaluation table and for valid infix operators
> > (def +precedence+
> >     {'rem 5,
> >      '* 4,
> >      '/ 3,
> >      '+ 2,
> >      '- 1})
>
> What's the significance of this map being named with a leading and
> trailing plus? I haven't encountered that before.

This is a naming conventions for constants.
(def +faculty-of-5+ 120)
In principle everything that you (def ...) is a constant in Clojure.
It’s a style issue to help others to read your program.

André Thieme

unread,
Nov 30, 2008, 4:30:29 PM11/30/08
to Clojure
On 30 Nov., 20:09, Stuart Sierra <the.stuart.sie...@gmail.com> wrote:
> On Nov 28, 6:54 pm, Dmitri <dmitri.sotni...@gmail.com> wrote:
>
> > First of I'd like to say that I find Clojure to be an excellent
> > language, however I find the lack of infix operators makes reading
> > equations somewhat unnatural, eg:
>
> Hi, Dmitri,
>
> Glad you like Clojure!  There are Common Lisp packages like <http://www.cliki.net/infix> that do infix-conversion.  I don't find infix
> notation useful except when transcribing formulas, and fractions or
> roots usually force me to use parentheses anyway.  Sun has an
> interesting experimental language called Fortress, <http://
> projectfortress.sun.com/>, that uses real mathematical notation.

Although a standard reader macro for infix syntax would be a nice
thing to have in Clojure.
It could be #[...] or something like that. I don’t care if it is
#[] or i{} or $() or something else.
But it would be nice to be able to say
say #[14x⁶ - 2√π]
vs (- (* 14 (. Math (pow x 6))) (* 2 (. Math (sqrt Math/PI))))

Or #[Σlist + 19]
vs (apply + 19 list)

or (if #[∀x∈M1: x > 10 ∧ ∃y∈M2: y=4] ...)
vs (if (and (every? #(> % 10) M1) (some #(= 4 %) M2)) ...)

Randall R Schulz

unread,
Nov 30, 2008, 5:47:15 PM11/30/08
to clo...@googlegroups.com
On Sunday 30 November 2008 13:30, André Thieme wrote:
> ...

>
> Although a standard reader macro for infix syntax would be a nice
> thing to have in Clojure.
> ...

Am I the only person who thinks this is a dead-end proposal that should
be dropped because our BDFL will simply not consider it?

Rich? Will you let us know if this is something we can move on
from 'cause you're simply not going to do it?

I would personally surely hope that reader macros come ahead of infix
parsing...


Randall Schulz

Rich Hickey

unread,
Nov 30, 2008, 7:14:13 PM11/30/08
to clo...@googlegroups.com

On Nov 30, 2008, at 5:47 PM, Randall R Schulz wrote:

>
> On Sunday 30 November 2008 13:30, André Thieme wrote:
>> ...
>>
>> Although a standard reader macro for infix syntax would be a nice
>> thing to have in Clojure.
>> ...
>
> Am I the only person who thinks this is a dead-end proposal that
> should
> be dropped because our BDFL will simply not consider it?
>
>
> Rich? Will you let us know if this is something we can move on
> from 'cause you're simply not going to do it?
>

Yes please, move on. These things get proposed perennially for Lisps,
but gain no traction because they are not in fact enhancements. People
are free to write their own macros, but I'm not interested in infix
for Clojure.

Rich

Reply all
Reply to author
Forward
0 new messages