[MLton] Ad-hoc infix identifiers, Printf, and libraries

Vesa Karvonen vesa.karvonen@cs.helsinki.fi
Thu, 14 Jul 2005 17:58:46 +0300


Quoting "Wesley W. Terpstra" <wesley@terpstra.ca>:
[...]
> I really like this!

Thanks!

> One comment: they don't play well with 'o' or ':='.
> The problem is that those have infix 3, and < = > ... are infix 4.
> 
> Ideally, I think your infixes should be 3.5 ;-)

Concerning language design, when I first learned about infix declarations
I thought that it wasn't such a good idea to use explicit precedence
levels and that it would be better to just specify a partial ordering. The
syntax might look something like this:

  operator * precedes +

This way it would be possible to insert new operators between any two
operators with different precedences:

  operator * precedes ? precedes +

Or alternatively:

  operator * precedes ?
  operator ? precedes +

Also, I think that explicit precedence levels allow some unfortunate
expressions. For example, in C/C++, I think that it is usually somewhat
confusing when one mixes bitwise (<<, >>, &, |, ^) and arithmetic (+, -,
*, /) operations in a single expression without parentheses. In such rare
cases it would probably be less error prone to require the use of
parentheses. In other words, the fact that there is a precedence
relationship between the bitwise and arithmetic operators probably just
causes unnecessary pain.

As far as I can see (but I have never implemented the idea fully), parsing
a language with only a partial ordering of infix identifiers is only a
little bit more difficult than parsing a language with explicit precedence
levels. (The parser needs to maintain a representation of the partial
ordering and ensure that the ordering remains acyclic.) Most of the
complexity comes simply from having fixity declarations, because then the
syntax can not expressed as a (static) context free grammar (CFG).

> That way 'Word8.fromInt o Char.ord o s <\ String.sub' works.
> Also, 'x := s <\ String.sub' and 'x := s <\String.sub\> i' work.

Hmm... I agree, that looks like a good idea!

> For myself, I just changed 'o' and ':=' to 1, and your ops to 2&3.

I have always wondered about the precedence levels of := and o in SML.

In imperative languages, assignment usually has the lowest precedence
(ignoring statement separators and expression sequencing). The relatively
high precedence of := in SML is particularly odd, because := returns a
unit, which makes little sense to combine with anything except `before'.
So, it seems to me that the precedence level of := really should be 1.

The case for o is different. It doesn't seem to make much sense to use o
with any of the operators (except on the left hand side of `before')
defined by the Basis library in an unparenthesized expression. This is
simply because none of the other operators deal with functions. It would
seem that the precedence of o could be chosen completely arbitrarily from
the set {1, ..., 9} without having any adverse effects with respect to
other infix operators defined in the Basis library.

BTW, in Haskell, an infix application x `f` y has, by default, the highest
precedence. So, another alternative might be to use precedences 8 & 9 for
the application operators. However, I am not sure whether it is more
practical to have a low or high precedence. A higher precendence would play
nice with o, but would probably play less well with other operators, because
the results of other operators do make sense as parameters to the sectioning
and application operators.

> However, that's not going to play well with other code. =(

Yes, it definitely makes one think thrice before one changes the fixity of
an identifier defined in the Basis library. However, it would seem to me
that changing := and o to have the precedence 1 is unlikely to cause major
problems.

-Vesa Karvonen