Discussion:
A family of function application operators for Standard ML
(too old to reply)
Vesa Karvonen
2005-07-12 15:50:02 UTC
Permalink
Compared to the preludes or standard libraries of many other functional
languages, the Standard ML Basis library is rather frugal when it comes to
basic combinators for function composition, function application and infix
operators. I for one would like to see some general purpose operators for
notational convenience to be added to the Basis library. Towards that
goal, I briefly describe a family of simple function application operators
that I have found convenient (some I have used longer than others). None
of these operators is novel, but the family has some synergetic properties
and a degree of completeness.

Let's first take a look at the definitions of the operators:

infix 2 <\ (* Left section *)
fun (x <\ f) y = f (x, y)

infix 2 \> (* Left application *)
fun f \> y = f y

infixr 2 /> (* Right section *)
fun (f /> y) x = f (x, y)

infixr 2 </ (* Right application *)
fun x </ f = f x

infix 1 >| (* Left pipe *)
val op>| = op</

infixr 1 |< (* Right pipe *)
val op|< = op\>

The left and right section operators, <\ and />, are useful in SML for
partial application of infix operators. (Paulson's ML for the Working
Programmer (2nd. ed.) describes curried functions `secl' and `secr' for
the same purpose on pages 179-181.) For example,

List.map (op- /> y)

is a function for subtracting `y' from a list of integers and

List.exists (x <\ op=)

is a function for testing whether a list contains an `x'.

The left and right sectioning operators, <\ and />, together with the left
and right application operators, \> and </, provide a way to treat any
binary function (i.e. a function whose domain is a pair) as an infix
operator. Namely,

x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN = fN (... f2 (f1 (x0, x1), x2) ..., xN)

and

xN </fN/> ... x2 </f2/> x1 </f1/> x0 = fN (xN, ... f2 (x2, f1 (x1, x0)) ...) .

As the treatment of fixity declarations is a bit problematic in SML, this
technique can be an attractive alternative to the duplication of fixity
declarations or pollution of the top-level with ad-hoc infix identifiers.
These operators essentially approximate the Haskell syntax

x `f` y

for infix application.

The left and right application operators may also provide some notational
convenience on their own. In general,

f \> x1 \> ... \> xN = f x1 ... xN

and

xN </ ... </ x1 </ f = f x1 ... xN .

If nothing else, both of them can eliminate parentheses. For example,

foo (1 + 2) (3 * 4) = foo \> 1 + 2 \> 3 * 4 .

The left and right piping operators, >| and |<, are the same as the right
and left application operators, respectively, except the associativities
are reversed and the binding strength is lower. They are useful for piping
data trough a sequence of operations. In general,

x >| f1 >| ... >| fN
= fN |< ... |< f1 |< x
= fN (... (f1 x) ...)
= (fN o ... o f1) x

(modulo obvious differences in evaluation order).

The right piping operator, |<, is provided by the Haskell prelude as $.
The left piping operator is not provided by the Haskell prelude, AFAIK.

Much more could be spelled out about the use of these operators, but they
are probably best learned through personal use. I doubt there is any
chance of getting them to the Basis library, but at least you can't blame
me for not trying at all. ;)

-Vesa Karvonen
Matthias Buelow
2005-07-12 16:05:30 UTC
Permalink
Post by Vesa Karvonen
infix 2 <\ (* Left section *)
fun (x <\ f) y = f (x, y)
I'm somewhat doubtful of new operators that consist solely of
punctuation and that are not established mathematical symbols..
Post by Vesa Karvonen
x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN = fN (... f2 (f1 (x0, x1), x2) ..., xN)
... because it quickly turns into something resembling line noise.

Maybe it's just me but I'd prefer concise short names, like, say
"lsect" instead of "<\".

mkb.
Vesa Karvonen
2005-07-12 18:03:03 UTC
Permalink
[...]
Post by Matthias Buelow
Post by Vesa Karvonen
x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN = fN (... f2 (f1 (x0, x1), x2) ..., xN)
... because it quickly turns into something resembling line noise.
The above shows the general pattern. In practise, identifiers are usually
longer, particularly identifiers bound to functions, which makes the code
easier to read (than the general pattern above). For example,

foo <\plus\> bar .

For the purpose of infixing, I would ideally like to have

foo `plus^ bar ,

but ^ is already wasted on string concatenation by the Basis library.
Other combinations of the characters ` and ^ would be possible, but having
tried many of them, I found none that was clearly the best (visually). The
symbols <\, \>, </ and /> have a certain self-documenting appearance and
symmetry, which is why I ended up with them.
Post by Matthias Buelow
Maybe it's just me but I'd prefer concise short names, like, say
"lsect" instead of "<\".
Would `lsect' be a curried function (like `secl' in MLFTWP) or an
infixed operator?

I agree that when used just for sectioning, there isn't much difference
between the forms

(x secl op+)
(secl x op+)
(x <\ op+)

However, I would very much like to be able to use standard and concise
"infixing operators" (as well as sectioning combinators, infix or not).
Such operators should preferably be in the Basis library, because they
have to be defined at the top-level to be of any use. The point is to
significantly reduce the need for ad-hoc infix declarations.

-Vesa Karvonen
Albert Lai
2005-07-15 03:49:17 UTC
Permalink
Post by Vesa Karvonen
[...]
Post by Matthias Buelow
Post by Vesa Karvonen
x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN = fN (... f2 (f1 (x0, x1), x2) ..., xN)
... because it quickly turns into something resembling line noise.
The above shows the general pattern. In practise, identifiers are usually
longer, particularly identifiers bound to functions, which makes the code
easier to read (than the general pattern above). For example,
foo <\plus\> bar .
I want to commend this design of notation; I can see the designer has
put serious thought and experimentation into it. It is an easy
mnemonic to think of <\ and \> as left- and right-delimiters, and once
you get past that, it is easy to remember that <\f\> is an
infix-operator corresponding to the function f. The difference
between <\f\> and </f/>, and how to remember which one is left- and
which one is right-associative, can also be learned. The example

x0 <\f1\> x1 <\f2\> x2 ... <\fN\> xN

is straightforward to read. This notation may have other problems,
but at least it has got one bit right by design. On the other hand, a
sequence of English-wannabe words would look like one of those
randomly generated spam messages.

I am doubtful of any doubt to new notations if the doubt appeals to
established notations in mathematics. Quite some of those were ad hoc
inventions or short-sighted patchworks in the first place.

(The above seems to be a radical view but it is not, so let me say
more. I do not mean that all established notations must be thrown
away. I mean that a notation (new or old) should be judged on
technical and cognitive merits rather than status quo such as number
of books and teachers teaching it. I think that much is obvious and
rational. ("But most people are used to established notations!" is a
slippery slope in a functional programming newsgroup in a world full
of imperative programmers.))

Loading...